# Factory Sales API — complete documentation Generated from the v1 OpenAPI description. Canonical HTML at https://developer.factory.app/. --- Factory Sales API · v1 # Put your shop's sales data where the work happens. Create and read sales orders and quotes, customers, the supporting product catalogue, inventory levels and suppliers. Every request is authenticated with a bearer token and scoped to the company that issued it. No SDK required — it is JSON over HTTPS. Start the quickstart Browse the reference Quickstart Key to first response in about five minutes. Push an order in Take an order from your system and land it on the floor. Stay in sync Poll the change feeds and checkpoint on what moved. ## Base URL One host, one major version in the path. ```js https://api.factory.app/v1 ``` ## What v1 covers - **Orders** (15 operations) — Create sales orders, read them, edit their line items, set their labels, upload flashing drawings, and work with each order's conversation. - **Quotes** (15 operations) — Create quotes, read them, edit their line items, set their labels, upload flashing drawings, and work with each quote's conversation. - **Customers** (3 operations) — List, search and create the customers you reference on a sales document. - **Catalogue** (6 operations) — Products, product kits and flashing templates — the ids a line item has to reference. - **Search** (1 operation) — One ranked text search across products, kits and flashings. Preview. - **Company** (2 operations) — Your account settings and custom field definitions. - **Order statuses** (1 operation) — The workflow columns configured for your company, in display order. - **Labels** (1 operation) — The tags you can attach to quotes and orders. Managed in the Factory app; read here. - **Users** (2 operations) — The people in your company, with their hourly labour rates, for assigning labour lines. - **Inventory** (3 operations) — Read and update stock levels for your catalogue products, tracked per variant row and colour. - **Suppliers** (2 operations) — List your company's suppliers. Managed in the Factory app; read here. - **Webhooks** (8 operations) — Subscriptions that push order and quote events to an endpoint you host, with a delivery record for every attempt. Administrator-only. ## What v1 does not do yet Worth knowing before you design around it. None of these are permanent. Webhooks cover documents only Subscriptions carry order and quote events only. Nothing announces customer, catalogue or stock changes yet — re-read those when freshness matters, and keep a slow poll on the change feeds with an `updatedSince` checkpoint as the backstop. Tax is advisory Send a tax descriptor if you like, but the server applies your account's single configured rate and currency; `taxAmount` carries the tax it applied (the descriptor itself is not populated yet). A line sent without `isTaxFree` follows the customer's `disableTaxByDefault`, and a value you send is kept as sent. Some writes are app-only You can create orders, quotes and customers, edit line items on either document, set labels, and update stock levels. Workflow status, fulfilment and payment still move in the Factory app only, and show up on your next read. Rate limits unannounced Requests are limited and a 429 carries `Retry-After`. Concrete numbers are coming — back off and retry. --- Get started # Quickstart Four calls: prove the key works, resolve a customer, find something to sell, then write an order. Everything below runs against your live company, so start on an account you do not mind adding a draft order to. 1 ## Create a key in the Factory app Open Settings, then API keys, and create one. It is shown once. A key is scoped to the company that made it and carries that company's permissions, so treat it like a password: server side only, never in a browser or a mobile app. > Store it as `FACTORY_API_KEY` in your environment. Every sample on this site reads it from there. 2 ## Confirm the key and read your settings A singleton read keyed by the token. It is the cheapest way to prove authentication works, and it tells you the currency, tax rate and measurement system every later response is expressed in. ```bash curl https://api.factory.app/v1/company \ -H "Authorization: Bearer $FACTORY_API_KEY" ``` ```json { "company": { "companyId": "company_01k2m4p6r8t0v2x4z6a8c0e2g4", "name": "Coastal Canopies", "currency": "AUD", "taxRate": "0.10", "measurementSystem": "MEASUREMENT_SYSTEM_METRIC" } } ``` 3 ## Resolve a customer and a product An order line references ids, not names. Resolve both before you write. Catalogue search takes one text query and returns ranked hits across products, kits and flashings. Search is in preview, so it is not yet covered by the compatibility guarantees. ```bash curl "https://api.factory.app/v1/customers?companyName=Coastal%20Canopies" \ -H "Authorization: Bearer $FACTORY_API_KEY" curl "https://api.factory.app/v1/catalogue:search?query=colorbond%200.42" \ -H "Authorization: Bearer $FACTORY_API_KEY" ``` 4 ## Write a draft order Send the whole order in one call. Leave `isSubmitted` off while you are testing and it lands as a draft. Always send a `requestId` (a UUID you generate) — a repeat with the same key can never create a second order: it is rejected with a 409 that carries the original order’s id. A catalogue line needs only the product, the row price you want and a quantity: name, category, attributes, cost and the unit price come from the catalogue, the total is computed, and anything you send yourself is kept. ```bash curl -X POST https://api.factory.app/v1/orders \ -H "Authorization: Bearer $FACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "requestId": "d5f7a8c2-3e1b-4d9f-a6c0-8b2e4f1d7a3c", "customerId": "cust_01k2m4p6r8t0v2x4z6a8c0e2g4", "reference": "PO-88431", "lines": [ { "catalogue": { "productId": "prod_01k2m4p6r8t0v2x4z6a8c0e2g4", "rowPriceId": "rowprice_01k2m4p6r8t0v2x4z6a8c0e2g4", "pricing": { "quantity": "12" } } } ] }' ``` > **Next.** Read core concepts before you write anything real — money arrives as micros and every 64-bit number is a string. Then push an order in covers the full path including validation failures. --- Get started # Authentication One scheme: a bearer token in the `Authorization` header. There is no OAuth flow, no request signing, and no query-string key. ```js Authorization: Bearer fk_live_9Qb2vX7mR4tE1sYw8Kp3Dn6Lz0Hc5Ja ``` ## Scope A token identifies exactly one company. Every list is filtered to that company and every write lands inside it — you never pass a company id. Asking for a resource that belongs to someone else returns 404, not 403, so an id from another account is indistinguishable from one that does not exist. ## Administrator-only operations A few operations check the caller's role rather than just the company. `GET /v1/users/{userId}` is administrator-only and returns 403 otherwise. `GET /v1/users` works for everyone but omits email addresses unless the caller is an administrator — so an integration that needs emails needs an administrator's key. Webhooks are administrator-only across the board: every operation on subscriptions and deliveries returns 403 unless the token's user is an administrator. ## When it fails Status Meaning 401 Missing, malformed, or expired token. 403 Authenticated, but the operation needs a company administrator. 404 The id does not exist *in your company*. Also what you get for a quote id on an order endpoint. ## Handling keys - Server side only. A key in client code is a key you have published. - One key per integration, so you can revoke one without breaking the rest. - Rotate by creating the new key, deploying, then deleting the old one. Both work during the overlap. - A deleted key stops working immediately and every call with it returns 401. --- Concepts # Core concepts The things that will bite you if you skim. Read this once and the rest of the reference is obvious. ## Money is micros A Money object is an integer count of millionths of the currency's major unit, plus an ISO 4217 code. One dollar is 1,000,000 micros. There are no floats anywhere in pricing, and the micro count travels as a string. ```json { "amountMicros": "12340000", "currency": "AUD" } // $12.34 ``` Divide by 1,000,000 for display, never for arithmetic. Do the maths in micros with integers and format at the edge. ## Every 64-bit number is a string Including `amountMicros` and `orderNumber`. JavaScript loses precision above 2^53, so we never send those as JSON numbers. Quantities, discounts, percentages and markups are decimal strings too — a quantity of `"2.5"`, a discount of `"10.00"` meaning ten percent. ## Ids carry their type Every id is a prefix, an underscore, and a 26-character sortable suffix. The prefix tells you what you are holding, and sending the wrong kind is rejected rather than silently mis-resolved. `order_` · `quote_` · `line_` · `drawing_` · `cust_` · `contact_` · `label_` · `status_` · `prod_` · `prodrow_` · `rowprice_` · `pricelvl_` · `category_` · `kit_` · `kitrow_` · `kitcomp_` · `subkit_` · `flashing_` · `flashtpl_` · `material_` · `user_` · `company_` · `supplier_` · `whsub_` · `whdel_` · `whevt_` · `message_` · `attach_` > When an accepted quote becomes an order, the suffix is kept and only the prefix changes: `quote_ABC…` becomes `order_ABC…`. That is how you follow one document across both feeds. ## Enums travel as names Responses always return the upper-case name — `FULFILMENT_METHOD_DELIVERY`, not `2`. Integers are accepted on the way in, but send names. New values can appear inside v1, so treat an unrecognised name as unknown rather than crashing on it. ## Pagination Every list takes `pageSize` and `pageToken` and returns `nextPageToken`. Loop until it comes back empty. Tokens are opaque, may be bound to the query that minted them, and should never be stored or constructed. ```js let token = ""; do { const page = await get("/v1/orders", { pageSize: 100, pageToken: token }); handle(page.orders); token = page.nextPageToken; } while (token); ``` ## Idempotency Every create-style write — `CreateOrder`, `CreateQuote`, `CreateCustomer`, the add-line-item calls, and the message and attachment posts — takes a client-generated UUID in `requestId`. Requests carrying the same key are executed at most once: any repeat is rejected with a 409, and if the original request completed the error body's `resourceId` carries the id of the resource it created. Keys are scoped to the API key that sends them and retained for at least 24 hours. > **A 409 is a stop sign, not a replay.** The API never returns the original response on a repeat, and a 409 also covers an original attempt whose outcome is unknown — in flight, failed, or cut off. Read the created id from `resourceId` in the error body, or verify with a read (`ListCustomers` with the `companyName` filter, comparing the full name, `GetOrder`, `GetQuote`), then send a fresh key if you still need a new document — a key is consumed by its first attempt, success or failure. If the idempotency store is unavailable, requests carrying a key are rejected with 503; requests without one are unaffected. ## Archiving An archived document is hidden from every read by default, and stays read-only. Each endpoint opts back in differently, which is the part worth reading twice. Where What the flag does ListOrders?archived= Swaps which set you are listing. It is not a superset — `true` returns *only* archived orders. GetOrder?includeArchived= Without it, an archived order is a 404. With it, you can read the order but still not write to it. QueryOrders?includeArchived= Widens the feed to live **and** archived together. See the sync guide — this one is genuinely useful. Every document read through one of those flags carries `isArchived: true`. On a default read it is always false. Changing the flag partway through pagination invalidates your page token, so start the walk again. ## Labels A label is a small company-scoped tag — name and hex colour — that can sit on any number of quotes and orders. They are created and renamed in the Factory app; the API reads the list and sets which ones are attached. A rename keeps the id, so store the id and not the name. > **Setting labels replaces the whole set.** `PUT /v1/orders/{orderId}/labels` is not an append. The list you send becomes the complete set — labels you leave out are removed, and an empty list clears every one. Read the order first if you mean to add to what is already there. An unknown label id fails the whole call with 404 and changes nothing. You can also attach labels at creation time by passing `labelIds` on `CreateOrder` or `CreateQuote`, which saves a second call. Same validation: an unknown id fails the whole request with 404, before the document is created. > **Labelling on create is not atomic.** In the rare case the document is created and the labelling then fails, the error names the new document id and the document exists **without** its labels. Do not retry the create — you would get a second document. Read the id out of the error and attach the labels with `SetOrderLabels`. To find documents by label, pass `labelIds` to `ListOrders` or `ListQuotes`. It matches *any* of the ids you give rather than all of them, and combines with the other filters. Changing it mid-pagination invalidates your page token, same as the archive flag. Labels come back on a full read of a document, oldest attachment first. They are absent from line-write responses, so read the document if you need to see them. ## Measurements price the line On the lineal and square pricing strategies the priced amount comes from `measurements` — an array of measured pieces on the line's `pricing` object, and on each kit component. One entry is one piece size: `length`, `width` and `amount`. ```json { "productKit": { "components": [ { "productRowId": "prodrow_01hb7n3q5s7u9w1y3a5c7e9g1k", "pricingStrategy": "PRICING_STRATEGY_LINEAL_METRES", "measurements": [ { "length": "2.4", "amount": "3" }, { "length": "1.8", "amount": "1" } ] } ] } } ``` All three values are decimal strings in the strategy's native unit — metres for the `*_METRES` strategies, feet for `*_FEET`. `width` belongs to the square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. `amount` is how many pieces of that size and defaults to 1. On these strategies the measured amount is the priced quantity — `quantity` is not a factor. The one legacy exception is an on-the-fly line on an account without server-side derivation, where the old formula still multiplies by it. The server sums the pieces, applying your account's minimum-length setting on lineal strategies, to derive the amount that scales the line total. `asBuiltMeasurements` sits beside `measurements` everywhere it appears — what production actually cut, in the same shape and units. As-builts drive cost, margin and stock consumption, never the line price, and whenever you omit them (create and update alike) they default to `measurements`. Send them only when the actual figures differ from what was quoted. > **Kit components measure the same way.** Each component in `productKit.components` — and inside each of its `subKits` — carries its own `measurements` and `asBuiltMeasurements` pair, with the same rules as a line. The old contract's names are gone — `measurementItems` and `actualMeasurementItems` on lines, `initialMeasurementItems` and `sumLengths` on kit components — send the typed pair instead. ## Inventory is per-variant A stock entry is the position of one product variant row at one colour, never a product total. A product with several rows appears once per row — which is why a single-row product can look like a product total — and a colour-tracked product splits each row again, one entry per colour. Sum a product's entries when you need the whole product's position. Each entry names its variant: `thickness` and the same `attributes` pairs the catalogue row holds, so entries are tellable apart without a catalogue read. `available` is `onHand` minus `promised` and goes negative when more is committed than is on hand. All four quantities are decimal strings. `onHand` is the only value you can set — `promised` and `onTheWay` are computed from open orders and purchase orders, and `SetStockLevels` echoes the full recomputed set back. > **The row id is the catalogue join key.** `productRowId` on a stock entry is the same id the catalogue product's `rows[].productRowId` carries — join there when you need the variant's prices or colour options. `colour` is populated only for colour-tracked products; all colour splits of a row share its thickness and attributes but carry their own quantities. ## Versioning The major version is in the path. v1 is a stable contract: every published operation, field and value keeps its meaning and wire form for the life of the version. Inside v1 changes are additive and can ship at any time — new endpoints, new optional request fields and query parameters, new response fields, a list operation starting to populate a field it left empty, new enum values, new error types and codes, new webhook event types, limits raised or patterns widened, and new items marked Preview. Build for that: ignore fields you do not recognise, treat an enum value you do not recognise as “other” rather than failing, treat an unrecognised error type as a generic error of its HTTP status, keep page tokens opaque, and never parse an id beyond its prefix. Breaking changes never ship inside v1: removing or renaming anything, adding a required request field, changing a type, a JSON name, a default or a meaning, changing which status or error type an outcome returns, making an always-present field conditional, reducing what a list operation populates, tightening validation on inputs that succeed today, or changing documented pagination or ordering. They arrive under a new version path or, for a removal, at the end of a deprecation period — announced in the changelog and marked in the description with its replacement and end date, and kept working for at least 12 months. When a new major version ships, v1 stays served for at least 12 months after it is generally available. An endpoint or field whose description begins with “Preview:” is published for early use and is not yet covered by these guarantees; it can change or be withdrawn with 30 days’ notice in the changelog, and it graduates when the marker is removed. Catalogue search is in preview today. Key order, whitespace, the wording of messages, the contents of page tokens, concrete rate-limit numbers, response timing and any behaviour the documentation does not describe are never part of the contract. --- Concepts # Field filtering Reduce response payload size by requesting only the fields you need, using the camelCase JSON field names you already see in responses. Every endpoint supports them: as query parameters on reads, and as request body fields on creates, line adds, label writes and drawing uploads. ## Include fields Add `fields` to the query string with a comma-separated list: ```bash GET /v1/orders:query?pageSize=50&fields=orderId,statusName,createdAt ``` ```json { "orders": [ { "orderId": "order_abc", "statusName": "Draft", "createdAt": "2026-08-01T00:00:00Z" }, { "orderId": "order_def", "statusName": "Pending", "createdAt": "2026-08-02T00:00:00Z" } ], "nextPageToken": "eyJv..." } ``` Envelope keys such as `nextPageToken` are always preserved — paths are relative to the resource, not the envelope. ## Exclude fields Use `excludeFields` to drop specific fields and keep everything else: ```bash GET /v1/quotes:query?pageSize=50&excludeFields=lines,notes ``` ## Nested fields Use dot notation to reach inside nested objects: ```bash GET /v1/orders:query?fields=orderId,total.amountMicros ``` ```json { "orders": [ { "orderId": "order_abc", "total": { "amountMicros": "50000000" } } ], "nextPageToken": "eyJv..." } ``` ## Paths across arrays A path that crosses an array is applied to every element. `labels.name` keeps `name` on every label and drops the rest of each one: ```bash GET /v1/orders:query?fields=orderId,labels.name ``` ```json { "orders": [ { "orderId": "order_abc", "labels": [ { "name": "Rush" }, { "name": "Trade" } ] } ], "nextPageToken": "eyJv..." } ``` ## Detail endpoints Filtering works identically on single-resource GETs. You do not need to name the wrapper key: ```bash GET /v1/quotes/quote_abc?fields=quoteId,total.amountMicros ``` ```json { "quote": { "quoteId": "quote_abc", "total": { "amountMicros": "22000000" } } } ``` ## Write operations Creates, line adds, label writes and drawing uploads take the same two options, but as fields on the request body rather than in the query string. A `?fields=` query parameter on these endpoints is ignored: ```bash POST /v1/orders { "customerId": "cust_abc", "reference": "PO-4471", "lines": [ ... ], "fields": "orderId,statusName" } ``` ```json { "order": { "orderId": "order_abc", "statusName": "Draft" } } ``` ## Rules - Field names are **camelCase** — the same names you see in responses. - Dot paths map across arrays. `labels.name` keeps `name` on every element of `labels`. - If both `fields` and `excludeFields` are supplied, `fields` wins and the other is ignored. It is not an error. - Naming a field that does not exist on the resource is a no-op — silently omitted, not rejected. - Filtering applies to `2xx` JSON responses only. Error bodies are never filtered. --- Concepts # Errors Every error response carries the same envelope, whatever the status: a short stable `type` naming the kind of failure, a human-readable message, and your echoed request id. On validation failures, `errors` lists one entry per field-level problem; on an idempotency conflict it holds one entry whose code says which kind, and `resourceId` carries the id the original request created; on every other kind of failure it is empty. ```json { "type": "validation_failure", "message": "The request could not be processed.", "requestId": "d5f7a8c2-3e1b-4d9f-a6c0-8b2e4f1d7a3c", "errors": [ { "code": "product_not_found", "message": "No product exists with that id.", "param": "lines[2].catalogue.product_id" } ] } ``` ## Reading it - `type` is the first thing to branch on — `validation_failure`, `not_found`, `failed_precondition`, `conflict`, `rate_limited`. Both `validation_failure` and `failed_precondition` are HTTP 400, but the first means bad input and the second means the document is in a state that forbids the change. Treat an unrecognised type as a generic error. - `param` points at the offending field in snake_case, indexed into arrays. Map it back to your own line numbers so an operator sees which row is wrong. - `code` is stable and safe to branch on. The list is not exhaustive and will grow, so treat anything you do not recognise as a generic validation failure — never as a hard crash. - All the problems in one request come back together. Show the operator the whole list, not just the first. ## Stable codes so far | HTTP | Type | When | | --- | --- | --- | | 200 | — | The call succeeded. | | 400 | validation_failure | Validation failed. The envelope's errors list has one entry per field-level problem. | | 400 | failed_precondition | The document is in a state that forbids the change — editing a line on an invoiced order. | | 401 | unauthenticated | Missing, invalid or expired bearer token. | | 403 | permission_denied | The operation needs a company administrator. | | 404 | not_found | No such id in your company — including a quote id on an order endpoint. | | 409 | conflict | An idempotency key was repeated: the original request completed (resourceId in the error body carries the id it created), its outcome is unknown, or the body differed. Verify with a read, then use a fresh key. | | 429 | rate_limited | Throttled. Wait for Retry-After, then retry. | | 429 | resource_exhausted | A size limit was exceeded (request or response too large). Retrying unchanged will fail; send less or request a smaller page. | | 501 | not_implemented | The input is not supported yet — adding a flashing line to an existing order, replacing or removing one, or creating an order or quote with companyName instead of customerId. | | 503 | unavailable | The idempotency store is unavailable. Only requests carrying a requestId are rejected; requests without a key are unaffected. | --- # API conventions Create and read sales orders and quotes, customers, and the supporting product catalogue, for your company. Every request is authenticated with a bearer token and is scoped to the authenticated company. ## Errors Every error response, whatever the HTTP status, has the same body — an error envelope with a short stable "type" naming the kind of failure, a human-readable "message", the request's idempotency key echoed back as "requestId" when the failing request carried one, and an "errors" array. The "type" values are "validation_failure" (HTTP 400 — a malformed or invalid request), "failed_precondition" (HTTP 400 — the request was valid but the resource's state does not allow it, for example writing a line on an invoiced order), "unauthenticated" (HTTP 401), "permission_denied" (HTTP 403), "not_found" (HTTP 404), "conflict" (HTTP 409), "rate_limited" (HTTP 429 — you are being throttled, and the Retry-After header, when present, says when to come back), "resource_exhausted" (HTTP 429 — a limit other than a request rate was exceeded, in practice a request or a response too large to carry; the message names the limit, and retrying unchanged will fail the same way — send less, or ask for a smaller page), "internal" (HTTP 500), "not_implemented" (HTTP 501 — a valid request the API does not support yet), "unavailable" (HTTP 503), and "timeout" (HTTP 504); treat an unrecognised type as a generic error. On a validation failure the errors array carries one entry per field-level problem — each with a stable error code, a human-readable message, and a pointer to the offending field (for example "lines[2].catalogue.product_id"). On an idempotency conflict (HTTP 409, type "conflict") it carries one entry whose code says which kind: "request_replayed" (the original request completed — "resourceId" on the envelope holds the id it created; read it instead of retrying), "request_id_reused" (the key was reused with a different body), "request_attempt_failed" (the original attempt failed and may still have written) or "request_in_progress" (its outcome is unknown); verify with a read before sending a fresh key. For every other type the array is empty. Stable validation codes defined so far include "customer_not_found" (the customer reference on a create could not be resolved) and "product_not_found" (a catalogue line references a product id that does not exist). The list is not exhaustive — more codes will be added as validations grow, so treat an unrecognised code as a generic validation failure rather than an error. ## Unknown request fields A JSON property the API does not recognise — a misspelt or removed field name — is silently ignored, not rejected, so check field names against this document. A request without a valid bearer token returns HTTP 401 (unauthenticated). Retrieving a single resource by an id that does not exist in your company returns HTTP 404 (not_found). ## Rate limits Requests are rate limited. A request over the limit returns HTTP 429 (rate_limited) with a Retry-After header saying how long to wait before retrying; concrete limits will be announced. ## Field filtering Reduce response payload size by requesting only the fields you need. Pass a `fields` query parameter with a comma-separated list of camelCase JSON names — e.g. `?fields=orderId,total.amountMicros`. Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. To keep everything except a few heavy fields, use `excludeFields` instead. If both are provided, `fields` takes precedence. Filtering applies to successful (2xx) JSON responses only — error bodies are never filtered. Unknown field names are silently ignored. ## Webhooks Subscribe with WebhookService to be notified when orders and quotes change. Deliveries are thin events: the signed JSON body names the event (`id`, `type`, `occurred_at`) and the resource by id (`data.order.id` or `data.quote.id`, plus `data.previous` on transitions) — it never carries the resource itself. Fetch current state with GetOrder / GetQuote, deduplicate on the event `id` (delivery is at-least-once), and verify the `X-Factory-Signature` header with the secret CreateWebhook returns once. The change feeds (QueryOrders, QueryQuotes with an updatedSince checkpoint) remain the way to backfill and reconcile. Money, enums and the versioning policy are explained in [Core concepts](https://developer.factory.app/concepts.md). --- Guide # Push an order in from another system You have an order in a CRM, an ERP, or a spreadsheet, and you want it on the floor in Factory. This is the whole path: resolve, build, write, recover. ## 1 · Resolve the customer List with the `companyName` filter — a case-insensitive contains-match, so compare each hit's full company name with the one you hold before taking its id; one hit is not proof of an exact match. No hits, create the customer first. Company name and email are required and the name must be unique within your company. ```bash GET /v1/customers?companyName=Riverside%20Roofing 200 { "customers": [{ "customerId": "cust_01k2m…", … }] } ``` ## 2 · Build the lines A line is one of six kinds and you set exactly one. Which you reach for depends on how well the thing you are selling is modelled in Factory. | Line kind | Use it when | | --- | --- | | catalogue | The item exists in your Factory catalogue. productId is required; rowPriceId refines to a price level. | | productKit | A configured kit. Send the component tree, echoing each component name through. Its components and sub-kits appear on the documents your company’s kit display settings name unless you send displayKitItems and displaySubKits yourself. | | flashing | A flashing template at a fixed thickness, with a colour from its selectable list, its drawing (required) and its cut list. Must be included at creation — it cannot be added to an existing order, replaced or removed later. | | labour | Time on the job, assigned to an active company user (labourUserId is required). Hours go in quantity, the charge rate in unitPrice and the cost rate in cost; the user's own hourly rates are not applied, and an omitted cost is stored as zero. | | onTheFly | A one-off that is not in the catalogue. You supply name and price yourself. | | notes | Free text on the document, either internal-only or customer-visible. | Do not send order totals. Subtotal, tax and total are derived by the server from the lines, and it will recompute them whatever you send. Line totals are different: each priced line carries its own `totalPrice`, computed as quantity times unit price times (1 minus discount over 100), rounded half up to the cent. Once server-side derivation is enabled for your account you can omit `totalPrice` — and on catalogue lines even `unitPrice`, sending `cost` and `markup` instead — and the server prices the line for you. A value you do send is checked, and a mismatch comes back as a 400 naming the line instead of being stored. Note the server keeps `markup` and `margin` consistent with the stored prices whenever `cost` is set — you will not always read back the values you sent. > **Three line kinds never derive.** Flashing lines, custom-formula lines and custom-priced kits are stored exactly as you send them — always compute and send their `totalPrice`. A standard kit derives its parent total as the sum of its components, so every priced component must carry its own `totalPrice`. Measurement-priced lines are priced from `measurements` alone — quantity is not a factor. `asBuiltMeasurements` is optional and defaults to `measurements`; send it only when production cut something different from what was quoted. ## 3 · Write it once, safely Generate a UUID and send it as `requestId`. A network timeout is then harmless: the API executes a key at most once, so a repeat can never put a duplicate on the floor — it comes back as a 409 whose error body carries the id of the order the first attempt created. Read the id out of the error, or verify with `GetOrder`. ```bash POST /v1/orders { "requestId": "d5f7a8c2-3e1b-4d9f-a6c0-8b2e4f1d7a3c", "customerId": "cust_01k2m…", "reference": "PO-88431", // searchable later "isSubmitted": true, // false leaves a draft "lines": [ … ] } ``` ## 4 · Recover from a rejection A 400 lists every field-level problem at once, each with a `param` pointing at the field. `customer_not_found` and `product_not_found` are the two you will hit most, and both mean your resolution step went stale. Re-resolve and retry with a fresh request id — a key is consumed by its first attempt, even one that failed, so repeating it answers 409. Full detail on the errors page. ## 5 · Attach drawings, if you have flashings Flashing lines must carry their drawing, and they are the one line kind that must be present at creation — adding one to an existing order, replacing it or removing it is refused with 501. Include them up front (or on the quote before conversion). The create response returns a `drawings` list, one entry per flashing line in the order you sent them, pairing your `tempId` with the assigned `drawingId` — address `UploadDrawingSvg` from it directly, no re-read needed. Then upload the rendered SVG for each. Every drawing reports its own outcome, and one that is not part of the order, not `image/svg+xml`, or already deleted is skipped rather than failing the batch. For the geometry itself and the figures you must compute, see the flashing guide. > **After the write.** Line items stay editable — add, replace or remove them, and totals recompute each time. Once the order is invoiced those calls are refused. Workflow status, fulfilment and payment move in the Factory app only, and you see them on your next read. To follow the order from here, use the change feed. --- Guide # Resolve customers and catalogue Nothing on a sales document is written by name. Before you can build a single line you need ids — and for catalogue lines, the right id out of four that look alike. ## Customers List with `companyName` to resolve by name — a case-insensitive contains-match, so compare each hit's full company name before taking its id. List entries are summaries — id, company name, billing address, last order time. If you need the email, contacts, tax identifier or price level, fetch the full record with `GetCustomer`. Cache the id against your own customer record; names get edited, ids do not. `CreateOrder` and `CreateQuote` take `customerId` only: a request that sends `companyName` instead is refused with 501 (`not_implemented`). ## Search first, then drill in One query across products, kits and flashings, returning minimal ranked hits. Every whitespace-separated word has to match something: a name, a category, a material, or an attribute value such as an item code. Then fetch full detail by the hit's id. > Result order is relevance-based and can change between identical requests. It is not a stable contract — never cache position, and never auto-pick hit one without a human or an exact item-code match behind it. ## Which id goes on the line This is the step that trips people up. A product has variant rows; a row has a price at each of your price levels. The line requires the product id. The row and row-price are optional: name them and the line takes the row’s attributes, cost and its price at that level, with name and category from the product. Anything you send yourself is kept as sent. | Id | What it identifies | | --- | --- | | prod_ | The product. Required on every catalogue line. | | prodrow_ | One variant, identified by attributes such as thickness and size. Optional: the line takes its attributes, cost and price. | | rowprice_ | That variant at one of your price levels. Optional: prices the line at that level, and sent alone it names the row too. | | pricelvl_ | The price level itself — Standard, Account. Not a line reference. | | flashtpl_ | One flashing at one thickness, carrying the colours you can pick from. | Send `priceLevelId` where `rowPriceId` is expected and it is rejected — different prefixes, deliberately. A rowPriceId must belong to the productRowId on the same line, and sent on its own it names its row. Omit both and the line is stored against the product alone: name and category are filled, nothing row-level is, and the unit price is yours to send. ## Colour is per line, not per variant Variant rows are identified by attributes like thickness and size. Colour usually is not one of them — it is chosen per line from the row's `colourOptions`, which come from the material, so every row sharing a material offers the same list. An empty list means the product has no colour choice at all. The same rule applies to flashings, where the selectable colours sit on the template. ## Kits and flashings Listings are deliberately light: `ListProducts` and `ListKits` leave rows and component trees empty. Call `GetProduct` or `GetKit` for the detail. Flashing templates are one flashing at one thickness — to change thickness you change template, not a field. And when you build a kit line from a template, echo the component's `productName` through: component names are stored as given and are not derived from the product. --- Guide # Keep another system in sync Webhooks announce order and quote changes the moment they happen; the query feed is the ledger that makes your copy complete. Build the loop on the feed first, then let deliveries trigger it. ## Use the query feed, not the list Both endpoints return orders. Only one is safe to page through while things are changing underneath you. GET /v1/orders Most recently updated first Good for looking one order up. The ordering shifts as orders change, so a document can move between pages while you are reading. GET /v1/orders:query Oldest change first Stable ordering plus an `updatedSince` window. This is the one to build a poller on. ## The loop Ask for everything since your checkpoint, page to the end, then move the checkpoint to the last document you actually processed — not to “now”. Overlap the window by a minute or two: `updatedSince` is inclusive, so re-seeing one document is normal and your handler should be idempotent anyway. ```js let since = checkpoint.read(); // e.g. 2026-07-29T02:00:00Z let token = ""; do { const page = await get("/v1/orders:query", { updatedSince: since, pageToken: token, pageSize: 100 }); for (const order of page.orders) { upsert(order); // keyed on orderId since = order.lastUpdatedAt; } token = page.nextPageToken; } while (token); checkpoint.write(since); ``` ## Let webhooks trigger the loop Subscribe an HTTPS endpoint with `POST /v1/webhooks`, picking the event types you care about — created, updated, status changes and archival on orders; created, updated, status changes and conversion on quotes. Store the `whsec_` secret from the create response: it is shown exactly once, and the full string is the HMAC-SHA256 key for the `X-Factory-Signature` header on every delivery. Webhook operations are administrator-only — subscribing, listing deliveries, all of it — so run this part of the loop with an administrator's key. Treat a delivery as a trigger, not a source of truth — the body names the document that changed by id (`data.order.id` or `data.quote.id`, plus `data.previous` on a transition) and never carries the document itself: acknowledge it with a 2xx, then run the same checkpointed loop. Failed deliveries retry for about ten hours across six attempts before they exhaust, and a subscription with nothing delivered successfully for seven days is disabled until you re-enable it — so keep a slow timer on the loop as the backstop. `GET /v1/webhook-deliveries` lists every attempt with the status your endpoint returned, which makes a missed event a lookup rather than a guess. ## Two feeds, one document Quotes and orders are separate feeds and a document lives in exactly one at a time. Poll both, with their own checkpoints. > **The migration to expect.** When a quote is accepted in the Factory app it becomes an order. It leaves the quote feed with no tombstone — no final deleted record, it simply stops updating — and appears on the order feed sharing the same 26-character suffix. A quote that goes quiet is reconciled by swapping `quote_` for `order_` and checking the order feed for that id. ## Backfilling a bounded window Pair `updatedSince` with `updatedBefore` to walk a closed window rather than everything up to now — the way to backfill history in chunks without one enormous run, or to re-pull a single day you suspect you mishandled. The lower bound is inclusive, the upper bound exclusive, so consecutive windows tile without overlapping. ```bash GET /v1/orders:query?updatedSince=2026-07-01T00:00:00Z&updatedBefore=2026-08-01T00:00:00Z ``` ## Archiving arrives in the feed Pass `includeArchived=true` and the feed carries live and archived orders together. Because archiving stamps the order's update time, you see it happen in-band: the order comes round again with `isArchived: true` and a fresh timestamp, and a restore comes round with false. Your existing upsert handles both — there is no second feed to reconcile and no deletion to infer. Leave the flag off and archiving looks like a document that simply stopped updating, which is indistinguishable from one that is merely quiet. If your mirror needs to know, turn it on. ## What never shows up - Work-in-progress documents that have not yet become a quote or an order. - Quotes on the order feed, or orders on the quote feed. Ever. - Deletions, as anything other than an absence. ## Narrowing the feed On top of the update window, both feeds accept a creation-time window — `createdAfter` (inclusive) and `createdBefore` (exclusive) — which combines with it: recent changes to orders created in a period. The order feed also filters by customer, reference substring, draft or submitted, workflow status id or name, payment status, received status, fulfilment method, and a required-by window (`requiredAfter`/`requiredBefore`). Discover your account's status ids from `GET /v1/company/order-statuses` — they are per-account, so do not hard-code them. --- Guide # Build a flashing line A flashing is a folded sheet-metal profile, drawn by hand and cut to a list of lengths. The API stores a flashing line exactly as you send it and never prices or checks it, so this guide works one through from the sketch: the numbers you compute, the drawing geometry, the write, and the SVG upload. ## 1 · Start from the sketch The example is a Colorbond flashing in Basalt at 0.55 mm: a 10 mm crush fold on the top edge, a 110 mm upright, a 90° bend, a 320 mm flat, a 140° bend and a 10 mm kick. The cut list is three pieces at 1200 mm and two at 925 mm, at $20.00 a metre. Resolve the template first — page `ListFlashings` and match on `flashingName`, `thickness` and `colours` — and keep its `flashtpl_` id. Every flashing template reports the same `productType`, so that filter does not narrow the list. Lengths and girths are always millimetres and `totalLength` is always metres, whatever measurement system the account uses. ## 2 · Work out the numbers Four values on the line are yours to compute. The server stores them as sent and never cross-checks them against the drawing or the cut list. | Field | Rule | This flashing | | --- | --- | --- | | totalGirth | Every edge size in the drawing plus every finish size, in millimetres. With other sides, the side with the largest girth. | 110 + 320 + 10 + 10 = 450 | | bends | One for each point that joins two edges, plus what each finish adds: crush fold 2, open hook 2, feather 1, drip edge 1. | 2 joins + a crush fold = 4 | | totalLength | The sum over the cut list of amount × length, divided by 1000, in metres. | 3 × 1200 + 2 × 925 = 5450 mm = 5.45 | | totalPrice | unitPrice × the priced metres, rounded to the cent. | $20.00 × 5.45 = $109.00 | > **Mind the minimum length.** If the account charges a minimum flashing length, every piece shorter than it is priced as that length. With a 1000 mm minimum the two 925 mm pieces price as 1000 each, so the priced length is 5.6 m and `totalPrice` is $112.00 — while `totalLength` stays 5.45. The API does not apply the minimum for you. ## 3 · Draw it A drawing is a set of points joined by edges. The points carry the sketch's canvas coordinates, which only lay it out; the sizes in `lines.values` are the real dimensions, so send `isFreeDrawing` as true. Record every edge at both ends — `p1.vectors` lists `p2` and `p2.connect` lists `p1` — key each size `"from-to"`, and give every edge a size. Angles are keyed by the point they sit on. A finish sits on the point at the finished end. `flip` chooses the side: looking from that end along its edge, false folds to the right and true to the left — here true turns the crush fold into the inside of the L. The arrow marks the front face; 0 points left and the angle turns clockwise on screen, so 135 points up and to the right. Give the drawing a `tempId` that no other drawing in the request uses, and number it 0 — the server does not assign `drawingNumber`, and documents sort drawings by it. Its side defaults to the far side; any `otherSides` default to the near side. ## 4 · Write the order Send the line with the document. A flashing line cannot be added to an existing order, replaced or removed, so it goes in the create call — or onto a quote before conversion. `priceLevel` is a name, not an id. `thickness` is stored as sent and never compared with the template, so make it match. Amounts are micros of the account currency. ```bash POST /v1/orders { "requestId": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f", "customerId": "cust_01k2m…", "isSubmitted": false, "lines": [{ "flashing": { "templateId": "flashtpl_01k2m…", "productName": "Colorbond", "priceLevel": "A", "colour": "Basalt", "thickness": "0.55", "bends": "4", "totalGirth": "450", "totalLength": "5.45", "subitems": [{ "amount": 3, "length": 1200 }, { "amount": 2, "length": 925 }], "unitPrice": { "amountMicros": "20000000", "currency": "AUD" }, "totalPrice": { "amountMicros": "109000000", "currency": "AUD" }, "drawing": { "tempId": "flashing-1", "drawingNumber": 0, "isFreeDrawing": true, "points": { "p1": { "id": "p1", "x": 150, "y": 175, "vectors": ["p2"], "connect": [], "finish": { "type": "FINISH_TYPE_CRUSH_FOLD", "size": 10, "label": "cf10", "flip": true } }, "p2": { "id": "p2", "x": 150, "y": 230, "vectors": ["p3"], "connect": ["p1"] }, "p3": { "id": "p3", "x": 310, "y": 230, "vectors": ["p4"], "connect": ["p2"] }, "p4": { "id": "p4", "x": 328.4, "y": 245.4, "vectors": [], "connect": ["p3"] } }, "lines": { "values": { "p1-p2": { "size": 110 }, "p2-p3": { "size": 320 }, "p3-p4": { "size": 10 } } }, "angles": { "values": { "p2": { "angle": 90 }, "p3": { "angle": 140 } } }, "frontArrow": { "x": 136, "y": 244, "angle": 135 } } }}] } ``` ## 5 · Map the drawing id and upload the SVG The create response pairs the `tempId` you sent with the assigned `drawingId` in its `drawings` list, one entry per flashing line in the order you sent them. ```json { "order": { "orderId": "order_01k2m…", "subtotal": { "amountMicros": "109000000", "currency": "AUD" } }, "drawings": [ { "tempId": "flashing-1", "drawingId": "drawing_01k2m…", "otherSides": [] } ] } ``` Render the drawing yourself and upload the image base64-encoded. The decoded SVG must be at most 2,000,000 bytes, and each drawing in the batch reports its own outcome. ```bash POST /v1/orders/{orderId}/drawings:uploadSvg { "svgs": [ { "drawingId": "drawing_01k2m…", "svg": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPuKApjwvc3ZnPg==" } ] } ``` ## 6 · What the server checks - A flashing line without a drawing is rejected with 400 (`validation_failure`). - Once the document is submitted, `templateId`, `priceLevel`, `colour`, `thickness`, `bends`, `totalGirth`, `unitPrice` and a drawing with points are required, and `bends` and `totalGirth` must be greater than zero. - `colour` is checked against the template's colours. Everything else — thickness, bends, girth, length, the cut list and every price — is stored exactly as sent. - A drawing whose points, lines or angles exceed about 15,000 characters of JSON is rejected with 400. > **After the write.** Read the order back with `GetOrder` to confirm the line, then open it in the Factory app. The drawing renders from the geometry you sent, and the app recomputes girth, bends and price from the same rules described here — if its figures differ from yours, the difference is in your numbers, and the stored values are what invoices will use. --- # API reference Factory Sales API v1 — 59 operations across 12 services. Base URL `https://api.factory.app/v1`. Each service is also published as a self-contained OpenAPI document — see [OpenAPI documents](https://developer.factory.app/openapi-documents.md). ## Catalogue - `GET /v1/catalogue/flashings` — [List flashings](https://developer.factory.app/reference/catalogue/list-flashings.md): Returns a paginated list of flashing templates. A flashing line item must reference one of these templates by id, so use this list to find the template for… - `GET /v1/catalogue/flashings/{templateId}` — [Get flashing](https://developer.factory.app/reference/catalogue/get-flashing.md): Returns the full flashing template, including its thickness and selectable colours. - `GET /v1/catalogue/kits` — [List kits](https://developer.factory.app/reference/catalogue/list-kits.md): Returns a paginated list of product kit summaries. This is a lightweight listing: rows, components, and sub-assemblies are not populated — use GetKit to… - `GET /v1/catalogue/kits/{kitId}` — [Get kit](https://developer.factory.app/reference/catalogue/get-kit.md): Returns the kit with its priced rows, top-level component products, and sub-assemblies — the single call that lets you drill into one kit's full detail. - `GET /v1/catalogue/products` — [List products](https://developer.factory.app/reference/catalogue/list-products.md): Returns a paginated list of catalogue product summaries. The listing is lightweight: rows is not populated — retrieve a product with GetProduct to get its… - `GET /v1/catalogue/products/{productId}` — [Get product](https://developer.factory.app/reference/catalogue/get-product.md): Returns the full product, including all of its priced variant rows. ## Search - `GET /v1/catalogue:search` — [Search catalogue](https://developer.factory.app/reference/search/search-catalogue.md): Searches products, kits, and flashings by text query and returns one ranked list of minimal hits, most relevant first. ## Company - `GET /v1/company` — [Get company](https://developer.factory.app/reference/company/get-company.md): A singleton read keyed by the bearer token. Returns the company's identity, locale and currency settings, the measurement system its quantities and… - `GET /v1/company/custom-fields` — [List company custom fields](https://developer.factory.app/reference/company/list-company-custom-fields.md): Custom fields are account-configured, typed fields on sales documents, customers, and suppliers. ## Labels - `GET /v1/company/labels` — [List labels](https://developer.factory.app/reference/labels/list-labels.md): Returns the labels configured for the authenticated company. Use the returned label ids when replacing an order's labels with SetOrderLabels. ## Order statuses - `GET /v1/company/order-statuses` — [List order statuses](https://developer.factory.app/reference/order-statuses/list-order-statuses.md): Returns the order statuses (workflow columns) configured for the authenticated company, in display order. ## Customers - `GET /v1/customers` — [List customers](https://developer.factory.app/reference/customers/list-customers.md): A company-scoped list of customers, optionally filtered by company name (a case-insensitive contains-match). - `POST /v1/customers` — [Create customer](https://developer.factory.app/reference/customers/create-customer.md): Adds a new customer — company details, addresses, and contacts — to your company. - `GET /v1/customers/{customerId}` — [Get customer](https://developer.factory.app/reference/customers/get-customer.md): Returns the full customer record — company details, addresses, contacts, and account flags — for the given id, scoped to your company. ## Inventory - `GET /v1/inventory` — [List inventory](https://developer.factory.app/reference/inventory/list-inventory.md): Returns a paginated list of stock entries — one per product row per colour — for every catalogue product that has inventory tracking enabled. - `GET /v1/inventory/{productId}` — [Get product inventory](https://developer.factory.app/reference/inventory/get-product-inventory.md): Returns every stock entry for the given product — one per row per colour. - `PUT /v1/inventory/{productId}` — [Set stock levels](https://developer.factory.app/reference/inventory/set-stock-levels.md): Replaces the on-hand quantity for each row+colour combination you include. ## Orders - `GET /v1/orders` — [List orders](https://developer.factory.app/reference/orders/list-orders.md): A flat, company-scoped list of orders. Confirmed orders only: quotes are read through QuoteService, and work-in-progress documents that have not yet become… - `POST /v1/orders` — [Create order](https://developer.factory.app/reference/orders/create-order.md): The primary write endpoint: submit a complete order — header, line items, adjustments, and addresses — in one call, either saved as a draft or submitted… - `GET /v1/orders/{orderId}` — [Get order](https://developer.factory.app/reference/orders/get-order.md): Returns the full order — header, lines, adjustments, totals, and server-derived fields — for the given id, scoped to the authenticated company. - `GET /v1/orders/{orderId}/attachments` — [List attachments](https://developer.factory.app/reference/orders/list-attachments.md): Returns every file on the order's conversation as a flat list, oldest first: files your team attached and documents the Factory app generated (isGenerated… - `POST /v1/orders/{orderId}/attachments` — [Upload attachment](https://developer.factory.app/reference/orders/upload-attachment.md): Uploads one file, up to 20 MB, into the order's conversation, where it appears immediately in the Factory app's Collaborate tab. - `GET /v1/orders/{orderId}/attachments/{attachmentId}` — [Get attachment](https://developer.factory.app/reference/orders/get-attachment.md): Returns the attachment's details and a fresh presigned download URL. - `DELETE /v1/orders/{orderId}/attachments/{attachmentId}` — [Delete attachment](https://developer.factory.app/reference/orders/delete-attachment.md): Removes the file from the order's conversation and from attachment lists. - `POST /v1/orders/{orderId}/drawings:uploadSvg` — [Upload drawing SVG](https://developer.factory.app/reference/orders/upload-drawing-svg.md): Drawings exist only on flashing lines: a drawing is the folded profile of a sheet-metal flashing, not a general image or file attachment for the order. - `PUT /v1/orders/{orderId}/labels` — [Set order labels](https://developer.factory.app/reference/orders/set-order-labels.md): The list you send becomes the order's complete label set: labels not listed are removed, and an empty list removes them all. - `POST /v1/orders/{orderId}/lines` — [Add order line item](https://developer.factory.app/reference/orders/add-order-line-item.md): Build an order up over time: each call creates one line under the order and recomputes the order's totals. - `PUT /v1/orders/{orderId}/lines/{lineId}` — [Update order line item](https://developer.factory.app/reference/orders/update-order-line-item.md): Send the full replacement line content; the whole line is replaced and the order's totals are recomputed. - `DELETE /v1/orders/{orderId}/lines/{lineId}` — [Delete order line item](https://developer.factory.app/reference/orders/delete-order-line-item.md): Identify the line by the server-assigned id returned when you read the order; the order's totals are recomputed after removal. - `GET /v1/orders/{orderId}/messages` — [List messages](https://developer.factory.app/reference/orders/list-messages.md): Returns the order's conversation — the same thread shown in the Factory app's Collaborate tab — oldest message first: text written by your team, files they… - `POST /v1/orders/{orderId}/messages` — [Post message](https://developer.factory.app/reference/orders/post-message.md): Adds a plain-text message to the order's conversation, visible immediately in the Factory app's Collaborate tab. - `GET /v1/orders:query` — [Query orders](https://developer.factory.app/reference/orders/query-orders.md): Like ListOrders, but ordered by when each order last changed (oldest first) and filterable by an updatedSince / updatedBefore time window — so a poller… ## Quotes - `GET /v1/quotes` — [List quotes](https://developer.factory.app/reference/quotes/list-quotes.md): A flat, company-scoped list of quotes. Quotes only — confirmed orders are read through OrderService. Quotes are returned most recently updated first;… - `POST /v1/quotes` — [Create quote](https://developer.factory.app/reference/quotes/create-quote.md): Submit a complete quote — customer, line items, adjustments, and addresses — in one call, either saved as a draft or finalised and sent. - `GET /v1/quotes/{quoteId}` — [Get quote](https://developer.factory.app/reference/quotes/get-quote.md): Returns the full quote — header, lines, adjustments, totals, and server-derived fields — for the given id, scoped to the authenticated company. - `GET /v1/quotes/{quoteId}/attachments` — [List attachments](https://developer.factory.app/reference/quotes/list-attachments.md): Returns every file on the quote's conversation as a flat list, oldest first: files your team attached and documents the Factory app generated (isGenerated… - `POST /v1/quotes/{quoteId}/attachments` — [Upload attachment](https://developer.factory.app/reference/quotes/upload-attachment.md): Uploads one file, up to 20 MB, into the quote's conversation, where it appears immediately in the Factory app's Collaborate tab. - `GET /v1/quotes/{quoteId}/attachments/{attachmentId}` — [Get attachment](https://developer.factory.app/reference/quotes/get-attachment.md): Returns the attachment's details and a fresh presigned download URL. - `DELETE /v1/quotes/{quoteId}/attachments/{attachmentId}` — [Delete attachment](https://developer.factory.app/reference/quotes/delete-attachment.md): Removes the file from the quote's conversation and from attachment lists. - `POST /v1/quotes/{quoteId}/drawings:uploadSvg` — [Upload drawing SVG](https://developer.factory.app/reference/quotes/upload-drawing-svg.md): Drawings exist only on flashing lines: a drawing is the folded profile of a sheet-metal flashing, not a general image or file attachment for the quote. - `PUT /v1/quotes/{quoteId}/labels` — [Set quote labels](https://developer.factory.app/reference/quotes/set-quote-labels.md): The list you send becomes the quote's complete label set: labels not listed are removed, and an empty list removes them all. - `POST /v1/quotes/{quoteId}/lines` — [Add quote line item](https://developer.factory.app/reference/quotes/add-quote-line-item.md): Build a quote up over time: each call creates one line under the quote and recomputes the quote's totals. - `PUT /v1/quotes/{quoteId}/lines/{lineId}` — [Update quote line item](https://developer.factory.app/reference/quotes/update-quote-line-item.md): Send the full replacement line content; the whole line is replaced and the quote's totals are recomputed. - `DELETE /v1/quotes/{quoteId}/lines/{lineId}` — [Delete quote line item](https://developer.factory.app/reference/quotes/delete-quote-line-item.md): Identify the line by the server-assigned id returned when you read the quote; the quote's totals are recomputed after removal. - `GET /v1/quotes/{quoteId}/messages` — [List messages](https://developer.factory.app/reference/quotes/list-messages.md): Returns the quote's conversation — the same thread shown in the Factory app's Collaborate tab — oldest message first: text written by your team, files they… - `POST /v1/quotes/{quoteId}/messages` — [Post message](https://developer.factory.app/reference/quotes/post-message.md): Adds a plain-text message to the quote's conversation, visible immediately in the Factory app's Collaborate tab. - `GET /v1/quotes:query` — [Query quotes](https://developer.factory.app/reference/quotes/query-quotes.md): Like ListQuotes, but ordered by when each quote last changed (oldest first) and filterable by an updatedSince / updatedBefore time window — so a poller… ## Suppliers - `GET /v1/suppliers` — [List suppliers](https://developer.factory.app/reference/suppliers/list-suppliers.md): Returns a paginated list of your company's suppliers. Useful for discovering supplier ids to pass as filters on the inventory list. Suppliers are ordered… - `GET /v1/suppliers/{supplierId}` — [Get supplier](https://developer.factory.app/reference/suppliers/get-supplier.md): Returns the supplier matching the given id. Useful when you already have a supplier id (for example from an inventory filter) and need its details without… ## Users - `GET /v1/users` — [List users](https://developer.factory.app/reference/users/list-users.md): A company-scoped list of users, returning active users by default. - `GET /v1/users/{userId}` — [Get user](https://developer.factory.app/reference/users/get-user.md): Returns the user record — name, email, and active status — for the given id, scoped to your company. ## Webhooks - `GET /v1/webhook-deliveries` — [List webhook deliveries](https://developer.factory.app/reference/webhooks/list-webhook-deliveries.md): Records cover the last 30 days. Filter by subscription, state, or a creation-time window; combine filters freely. - `GET /v1/webhook-deliveries/{deliveryId}` — [Get webhook delivery](https://developer.factory.app/reference/webhooks/get-webhook-delivery.md): Fetch one delivery record. - `GET /v1/webhooks` — [List webhooks](https://developer.factory.app/reference/webhooks/list-webhooks.md): Sorted by creation time, newest first. Secrets are never included. - `POST /v1/webhooks` — [Create webhook](https://developer.factory.app/reference/webhooks/create-webhook.md): Creates a subscription delivering the requested event types to an HTTPS endpoint. - `GET /v1/webhooks/{webhookId}` — [Get webhook](https://developer.factory.app/reference/webhooks/get-webhook.md): The secret is never included — it is returned only by CreateWebhook. - `DELETE /v1/webhooks/{webhookId}` — [Delete webhook](https://developer.factory.app/reference/webhooks/delete-webhook.md): Deliveries stop immediately. Existing delivery records remain readable until their 30-day retention lapses. - `PATCH /v1/webhooks/{webhookId}` — [Update webhook](https://developer.factory.app/reference/webhooks/update-webhook.md): Fields left unset keep their current value. The secret cannot be changed (rotation is not available in v1); to stop deliveries set status to disabled, to… - `POST /v1/webhooks/{webhookId}:test` — [Test webhook](https://developer.factory.app/reference/webhooks/test-webhook.md): POSTs a synthetic ping event through the real signing and delivery path and reports what the endpoint answered. --- # OpenAPI documents Machine-readable descriptions of the Factory Sales API v1, served byte-for-byte as they are generated from the contract. Each service below is a complete OpenAPI 3.0 document on its own — its paths, every schema they reference, bearer auth, servers, and the full API conventions text — sized to hand to an LLM (~5k–67k tokens) where the whole API (~162k tokens) is not. ## The whole API - [factory_api_v1.openapi3.json](https://developer.factory.app/openapi/factory_api_v1.openapi3.json): the entire v1 contract as one OpenAPI 3.0 document (~162k tokens). Also served at https://developer.factory.app/openapi.json. - [factory_api_v1.swagger.json](https://developer.factory.app/openapi/factory_api_v1.swagger.json): the same contract as a Swagger 2.0 document. ## One document per service | Service | Description | Operations | Approx. tokens | Updated | File | |---|---|---|---|---|---| | Catalogue | Your product catalogue — the products, kits, and flashings you sell. | 6 | ~19k | 2026-10-05 | [catalogue.openapi3.json](https://developer.factory.app/openapi/services/catalogue.openapi3.json) | | Company | Your company's settings and custom-field definitions — identity, currency and locale, the measurement system quantities are expressed in, tax context, and… | 2 | ~10k | 2026-09-16 | [company.openapi3.json](https://developer.factory.app/openapi/services/company.openapi3.json) | | Customers | Your company's customers — the businesses you quote and sell to. | 3 | ~12k | 2026-10-19 | [customer.openapi3.json](https://developer.factory.app/openapi/services/customer.openapi3.json) | | Inventory | Read and update stock levels for your catalogue products. Stock is tracked per product variant row, optionally split by colour. Each entry carries on-hand,… | 3 | ~10k | 2026-09-16 | [inventory.openapi3.json](https://developer.factory.app/openapi/services/inventory.openapi3.json) | | Labels | The labels your company uses to tag orders. | 1 | ~5k | 2026-09-16 | [label.openapi3.json](https://developer.factory.app/openapi/services/label.openapi3.json) | | Order statuses | The workflow statuses your company's orders move through. | 1 | ~5k | 2026-09-16 | [order-status.openapi3.json](https://developer.factory.app/openapi/services/order-status.openapi3.json) | | Orders | Create sales orders, read and list them, upload flashing-drawing SVGs, and work with each order's conversation (messages and file attachments) — the order… | 15 | ~67k | 2026-10-19 | [order.openapi3.json](https://developer.factory.app/openapi/services/order.openapi3.json) | | Quotes | Create quotes, read them, add, update, and remove their line items, upload flashing-drawing SVGs, and work with each quote's conversation (messages and file… | 15 | ~63k | 2026-10-19 | [quote.openapi3.json](https://developer.factory.app/openapi/services/quote.openapi3.json) | | Search | Preview: free-text search across your catalogue. This service is in preview — published for early use, not yet covered by the compatibility guarantees in… | 1 | ~6k | 2026-09-16 | [search.openapi3.json](https://developer.factory.app/openapi/services/search.openapi3.json) | | Suppliers | List your company's suppliers. Suppliers are managed in the Factory purchasing UI — this read-only surface lets API consumers discover supplier ids for use… | 2 | ~7k | 2026-09-16 | [supplier.openapi3.json](https://developer.factory.app/openapi/services/supplier.openapi3.json) | | Users | The people in your company, for assigning work to. | 2 | ~7k | 2026-09-16 | [user.openapi3.json](https://developer.factory.app/openapi/services/user.openapi3.json) | | Webhooks | Register HTTPS endpoints to be notified when orders and quotes change, and inspect what was delivered. | 8 | ~18k | 2026-09-16 | [webhook.openapi3.json](https://developer.factory.app/openapi/services/webhook.openapi3.json) | This table is generated from the manifest at [https://developer.factory.app/openapi/services/index.json](https://developer.factory.app/openapi/services/index.json) — fetch that to enumerate the documents programmatically. Every document carries the same operational contract in `info.description` — rendered once at [API conventions](https://developer.factory.app/conventions.md). --- # Changelog Changes inside v1 are additive. Anything breaking ships under a new version path, announced ahead of time. ## A line without isTaxFree follows the customer **20 Oct 2026** · Fixed A line written without isTaxFree is now exempt when the customer has disableTaxByDefault, as the CreateOrder description always said and as a line added in the Factory app is; on a catalogue line, a product marked tax-free in the catalogue exempts it too. A value you send, true or false, is kept as sent, and an update that omits the flag follows the customer again. The rule reaches every line kind, kit components and sub-kit components, on orders and quotes alike. On a kit, the components carry the customer default and the kit line’s own flag is read first: a kit line sent tax-free exempts the whole kit, an omitted one on a standard kit is false so each component’s own flag decides, and a custom-priced kit follows the customer and is taxed on the kit line alone. isTaxFree is now always present on reads, so you can see why tax was or was not charged. Two corrections: a flashing line is taxed at your account’s rate whether or not isTaxFree is set, and the tax descriptor on an order or quote is not populated — taxAmount carries the tax applied. The per-line isTaxFree: true workaround keeps working and is no longer needed. ## Catalogue lines are completed from the catalogue, and rows carry cost, markup and material **6 Oct 2026** · Changed A catalogue line now fills itself in the way the Factory app does: name and category from the product, attributes, cost and the unit price from the row you name (the price at rowPriceId, or the row’s marked-up price when you choose no level), and the pricing strategy from the product. Anything you send is kept as sent. A productRowId must belong to the product and a rowPriceId to the row; sent alone, a rowPriceId names its row. A bad reference is a 400 that names the field. GetProduct rows also return cost, markup, markupIsPercentage, materialId and materialName, so an integration can tell apart rows with identical attributes on a multi-material product. A kit line’s displayKitItems and displaySubKits are now stored as sent, and a kit that omits them takes your company’s “Display individual Product Kit items on” and “Display Product Kit sub-kits on” settings, as a kit built in the app does. The two fields list the eight display keys they accept. ## Flashing lines documented end to end, a created id on 409, and pricing settings **17 Sep 2026** · Added Every field on a flashing line and its drawing now says what goes in it: the cut list in subitems (millimetre numbers), totalLength in metres, totalGirth and bends with the rules Factory uses to count them, how the four money fields relate, the drawing's points, edges, angles, finishes, flip and arrow conventions, which side a drawing defaults to, and the size limits. The drawing is required. A new guide, Build a flashing line, works a hand-drawn flashing through end to end. A repeated tempId is now rejected instead of silently reusing the first drawing. The error envelope gains resourceId: on a 409 whose original request completed, it carries the id that request created, and the four 409 codes are listed. GetCompany returns pricingSettings — the minimum lengths charged on lineal-metre, lineal-feet and flashing lines, and the crush-fold bend count. Corrected wording: creating an order or quote with companyName instead of customerId returns 501; the customer name filter is a contains-match; a fee must be a fixed amount and a markup changes no total; the add-line response carries only the id and totals; amounts are never converted between currencies; unrecognised request fields are ignored. Removing a line whose cost and markup no longer agree with its price no longer fails. ## v1 is stable **16 Sep 2026** · Released The v1 contract is frozen: 59 operations across orders, quotes, customers, catalogue, inventory, suppliers, users, labels, order statuses, company settings, webhooks and search. From here every published operation, field and value keeps its meaning and wire form for the life of the version. Changes inside v1 are additive; anything breaking ships under a new version path or, for a removal, after a deprecation announced here. ## Order numbers are read-only **14 Sep 2026** · Changed purchaseOrder was never your purchase order number. It is Factory’s own sequential document number — the “Order #” in the app, on invoices and delivery documents, and the document reference in the accounting integrations — and the server has always assigned it. It is now orderNumber on Order and Quote: read-only, assigned when the document is created, and unchanged when a quote becomes an order. It is gone from CreateOrder and CreateQuote; a request that still sends purchaseOrder has it ignored, as with any field the API does not recognise. Your own purchase order number belongs in reference, which the app shows as “PO #” and the documents as “PO”; reference itself is unchanged. ## API versioning policy, and search marked preview **14 Sep 2026** · Added The API conventions now carry the full v1 API versioning policy: which changes are additive and can ship at any time, which are breaking and never ship inside v1, how deprecation works — announced here and in the description, with at least 12 months before removal — and a preview tier for items published early, which can change or be withdrawn with 30 days’ notice here. Catalogue search is the first preview item. It works today, with two known limits: a page-by-page walk of a large result set can drop or repeat a row, so fetch one large page when completeness matters, and a flashing hit’s id cannot yet be passed to GetFlashing. ## Idempotency keys are enforced **10 Sep 2026** · Changed requestId is now a real at-most-once guarantee on every write that takes it — CreateOrder, CreateQuote, CreateCustomer, the add-line-item calls, PostMessage and UploadAttachment. Requests carrying the same key are executed at most once; any repeat is rejected with 409 rather than replayed, and when the original request completed the error body carries the id of the resource it created. If the outcome of the original attempt is unknown — in flight, failed, or cut off — verify with a read, then retry with a fresh key: a key is consumed by its first attempt, success or failure. Keys are scoped to the API key that sends them and retained for at least 24 hours. CreateCustomer, previously documented as validated-but-not-deduplicated, now carries the same guarantee and gains its 409 response. When the idempotency store is unavailable, requests carrying a key are rejected with 503; requests without one are unaffected. ## Labour costs **4 Sep 2026** · Added Every user record now carries two default hourly rates: hourlyRateCharged, what an hour of their work sells for, and hourlyRateCost, what it costs your company — both zero until a rate is set in the Factory app. Orders and quotes gain labourTotal, a read-only rollup the server derives on every write: the sum of the labour line totals, including labour components inside product kits. It is the labour charge subtotal — what labour sells for, not what it costs; the cost side stays per-line, on the pricing cost of each labour line. Create responses do not include labourTotal; it is there on any subsequent read. ## Messages and attachments **2 Sep 2026** · Added Order and quote conversations — the thread the Factory app shows in its Collaborate tab — are now on the API, with the same operations on both services. ListMessages returns the thread oldest first, text and files together; ListAttachments flattens it to just the files, with isGenerated separating documents the app generated (quote and invoice PDFs) from uploads. Every attachment carries a presigned download URL that expires — re-read, or call GetAttachment, for a fresh one rather than storing it. UploadAttachment sends one file up to 20 MB, base64-encoded over REST, with an optional caption that becomes the message text; HEIC and HEIF images convert to JPEG on upload, and a requestId idempotency key makes retries safe. DeleteAttachment removes a file you uploaded — generated documents are protected, and a Factory app administrator can reverse the removal. PostMessage adds a plain-text message on its own; the text is stored as an HTML fragment — escaped and wrapped in a paragraph tag — and reads back in that form, so mention markup renders as literal text and notifies no one. To send a file with text, use the caption on UploadAttachment. ## Webhooks **1 Sep 2026** · Added A new WebhookService pushes order and quote events to an HTTPS endpoint you subscribe. Eight event types cover creation, updates, status changes, archival and quote conversion. A delivery names the document that changed and never carries it: the JSON body is id, type, occurred_at and data — data.order.id or data.quote.id (an order for quote.converted_to_order), plus data.previous on transitions (status_id, quote, or archived) — so read the document back with GetOrder or GetQuote; an archived order needs includeArchived. Every delivery is signed: the whsec_ secret from the create response, shown exactly once, is the HMAC-SHA256 key for the X-Factory-Signature header, and each request also carries X-Factory-Event (the type), Date (the same instant as the signed t, as an HTTP-date) and User-Agent: Factory-API/v1. Failed deliveries retry after 1 min, 5 min, 30 min, 2 h and 8 h — six attempts over about ten hours — before they exhaust, and a subscription with nothing delivered successfully for seven days is disabled until you re-enable it. Delivery records are queryable, so you can see what was sent, when, and what your endpoint said back — and a test operation sends a synthetic event on demand. ## Measurements get a real schema **20 Aug 2026** · Changed The loose measurement arrays on lines and kit components are now typed. measurements (was measurementItems) prices the line or component; asBuiltMeasurements (was actualMeasurementItems) records what production actually cut and drives cost, margin and stock — never the price. Both are arrays of MeasurementEntry: length, width and amount as decimal strings in the strategy’s native unit. Kit components drop initialMeasurementItems and sumLengths. asBuiltMeasurements is optional and defaults to measurements on create and update alike, so payloads that copied one into the other can simply stop. ## Kit totals now have to add up **20 Aug 2026** · Changed The server now checks the maths inside standard kits instead of trusting it. Component totals have to match their own quantity, price and discount (or their measurements), a parent or sub-kit total has to equal what its pieces add up to, and every priced component needs its own totalPrice even when you priced the parent yourself. Custom-priced kits always send a total now — leaving it out used to store $0.00 silently. One field to watch: customPrice overrides a component’s whole total, not its unit price, so if you send it alongside totalPrice the two have to agree. When something doesn’t line up you get a 400 naming the component with both numbers, so you can fix the payload instead of hunting through a stored document. ## Line totals can be derived server-side **18 Aug 2026** · Changed Rolling out account by account: with derivation enabled, omit a line’s totalPrice and the server computes it — and rejects a supplied total that does not match, naming the line, instead of storing it. Catalogue lines can also omit unitPrice and send cost plus markup. Flashing, custom-formula and custom-priced-kit totals are never derived — always send those. Until derivation is enabled for your account nothing changes. Field descriptions across LinePricing now spell out every rule. ## Stock entries identify their variant **12 Aug 2026** · Added Every stock entry now carries thickness and attributes — the same identifying name/value pairs the catalogue product row holds — so entries can be told apart without a catalogue lookup. Applies to ListInventory, GetProductInventory and the SetStockLevels response alike. Entries were always per variant row per colour, never product totals; the endpoint descriptions now say so explicitly. ## Inventory and supplier APIs **10 Aug 2026** · Added New InventoryService with three operations: list stock, get stock for one product, and set stock levels. New SupplierService with two read-only operations: list suppliers and get one supplier. Products now carry an isInventoryTracked flag indicating whether stock is tracked. ## More filters on the order and quote lists **7 Aug 2026** · Added ListOrders and QueryOrders accept statusName, receivedStatus, fulfilmentMethod, and a requiredAfter/requiredBefore window; ListOrders also gains statusId and paymentStatus, which QueryOrders already had. ListQuotes and QueryQuotes accept fulfilmentMethod, requiredAfter and requiredBefore. The two order endpoints now filter on nearly the same set. ## Field filtering on responses **7 Aug 2026** · Added Every endpoint can trim its response with fields and excludeFields — a comma-separated list of camelCase names, with dot notation for nested fields. Names that do not exist on the resource are ignored rather than rejected, and fields takes precedence when both are supplied. Reads take them as query parameters; creates, line adds, label writes and drawing uploads take them as request body fields instead. ## One error envelope for every failure **6 Aug 2026** · Changed Every error response now shares the same envelope with a stable type field — validation_failure, not_found, conflict, rate_limited. Creates that include flashing lines return a drawings mapping pairing each tempId with its assigned drawingId, so SVG uploads no longer need a re-read. The change feeds accept createdAfter and createdBefore. Adding a flashing line to an existing order is explicitly unimplemented and returns 501. ## Labels on create, and filtering by label **5 Aug 2026** · Added CreateOrder and CreateQuote accept labelIds, so a document can be labelled as it is created. ListOrders and ListQuotes accept labelIds to return only documents carrying at least one of the given labels. ## Editable order lines, labels and archiving **5 Aug 2026** · Added Order line items can now be added, replaced and removed after creation, matching quotes. Labels can be read with ListLabels and attached to orders and quotes. Archived documents are reachable through new archived and includeArchived flags, and the change feeds accept an updatedBefore upper bound. ## v1 draft-005 published **29 Jul 2026** · Draft Quote line item add, update and remove; catalogue search across products, kits and flashings; custom field definitions on the company endpoint. ## Change feeds for orders and quotes **11 Jun 2026** · Added QueryOrders and QueryQuotes join the list endpoints with a stable oldest-first ordering and an updatedSince window, so a poller can checkpoint safely. ## Flashing drawing uploads **02 May 2026** · Added Attach client-rendered SVGs to an order or quote after creation. Each drawing reports its own outcome. --- # GET /v1/catalogue/flashings **Service:** Catalogue **Operation:** `CatalogueService_ListFlashings` Returns a paginated list of flashing templates. A flashing line item must reference one of these templates by id, so use this list to find the template for the thickness you need and its selectable colours. Templates are returned ordered by flashing name, then thickness. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of templates to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `productType` | query | string | no | Optional: only return templates whose `productType` equals this value. Every flashing template currently reports "Flashing", so the filter narrows nothing today. There is no filter by name, thickness or colour: to find a template, page through the list and match on `flashingName`, `thickness` and `colours`. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListFlashingsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListFlashingsResponse` | Field | Type | Description | | --- | --- | --- | | `flashings` | Flashing[] | The flashing templates in this page. | | `flashings.templateId` | string | The template's unique id. Reference this on a flashing line item. | | `flashings.productType` | string | The catalogue product type of this template. Flashing templates report "Flashing"; it marks them apart from other kinds of catalogue product. | | `flashings.thickness` | string | The thickness this template is for, as a decimal string in the template's units (e.g. "0.55", millimetres for metric accounts). Fixed for the template: each thickness is a separate template with its own id, so to order a different thickness, select the template (`templateId`) that has it. | | `flashings.flashingId` | string | The id of the underlying flashing. | | `flashings.flashingName` | string | The display name of the underlying flashing. | | `flashings.materialId` | string | The id of the template's material selection. Not populated yet: empty for every template today, and may start being returned in a later release without a version change. Use `flashingId` to identify the flashing. | | `flashings.materialName` | string | The display name of the template's material selection. Empty today, like `materialId`. | | `flashings.colours` | string[] | The colours you can select for a flashing line built on this template. A line's colour must be one of these. Colour is the per-line choice; the thickness is fixed by the template id (see `thickness`). | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/catalogue/flashings/{templateId} **Service:** Catalogue **Operation:** `CatalogueService_GetFlashing` Returns the full flashing template, including its thickness and selectable colours. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `templateId` | path | string | yes | The id of the flashing template to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetFlashingResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No flashing template with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetFlashingResponse` | Field | Type | Description | | --- | --- | --- | | `flashing` | Flashing | The requested flashing template. | | `flashing.templateId` | string | The template's unique id. Reference this on a flashing line item. | | `flashing.productType` | string | The catalogue product type of this template. Flashing templates report "Flashing"; it marks them apart from other kinds of catalogue product. | | `flashing.thickness` | string | The thickness this template is for, as a decimal string in the template's units (e.g. "0.55", millimetres for metric accounts). Fixed for the template: each thickness is a separate template with its own id, so to order a different thickness, select the template (`templateId`) that has it. | | `flashing.flashingId` | string | The id of the underlying flashing. | | `flashing.flashingName` | string | The display name of the underlying flashing. | | `flashing.materialId` | string | The id of the template's material selection. Not populated yet: empty for every template today, and may start being returned in a later release without a version change. Use `flashingId` to identify the flashing. | | `flashing.materialName` | string | The display name of the template's material selection. Empty today, like `materialId`. | | `flashing.colours` | string[] | The colours you can select for a flashing line built on this template. A line's colour must be one of these. Colour is the per-line choice; the thickness is fixed by the template id (see `thickness`). | --- # GET /v1/catalogue/kits **Service:** Catalogue **Operation:** `CatalogueService_ListKits` Returns a paginated list of product kit summaries. This is a lightweight listing: rows, components, and sub-assemblies are not populated — use GetKit to fetch a single kit's priced rows and full component tree. Kits are returned alphabetically by name. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of kits to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `name` | query | string | no | Optional: only return kits whose name matches this text. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListKitsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListKitsResponse` | Field | Type | Description | | --- | --- | --- | | `kits` | Kit[] | The kits in this page. | | `kits.kitId` | string | The kit's id — the id of the catalogue product that fronts this kit. This is the id a kit line item references, and the id GetKit accepts. | | `kits.name` | string | The kit's display name. | | `kits.rows` | KitRow[] | The kit's priced variant rows. Populated by GetKit; the kit listing leaves this empty. | | `kits.rows.kitRowId` | string | The kit variant row's unique id. | | `kits.rows.attributes` | RowAttribute[] | Attribute pairs that identify this kit variant. | | `kits.rows.markedUpPrice` | Money | The computed marked-up sell price, for reference. | | `kits.rows.prices` | RowPrice[] | The variant's price at each of your price levels. | | `kits.components` | KitProductComponent[] | The kit's top-level component products (those not inside a sub-assembly). Populated by GetKit; the kit listing leaves this empty. | | `kits.components.kitProductId` | string | The component's unique id within the kit. | | `kits.components.productId` | string | The id of the catalogue product used as this component. Additional-product components only. | | `kits.components.productRowId` | string | The id of the specific product variant row used as this component. Catalogue-product components only. | | `kits.components.colour` | string | The component's colour. Catalogue-product components only. | | `kits.components.material` | string | The component's material. Catalogue-product components only. | | `kits.components.quantity` | string | The component's quantity, as a decimal string. | | `kits.components.productType` | KitComponentType | The component's kind: catalogue product, notes, or labour. Determines which of the fields below are populated. On-the-fly is never a kit-template component, so it is not part of this set. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `kits.components.notes` | string | Free-text note. Populated when this is a notes component. | | `kits.components.notesType` | NotesType | Whether the note is internal-only or customer-visible. Notes components only. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `kits.components.labourUserId` | string | The assigned company user's id. Populated when this is a labour component. | | `kits.components.hourlyRateCharged` | Money | The labour charge-out rate per hour. Labour components only. | | `kits.components.hourlyRateCost` | Money | The labour cost rate per hour. Labour components only. | | `kits.components.customHourlyRateCharged` | Money | A custom override of the charge-out rate, when set. Labour components only. | | `kits.components.customHourlyRateCost` | Money | A custom override of the cost rate, when set. Labour components only. | | `kits.components.productName` | string | The display name of the catalogue product used as this component. Catalogue-product components only. Echo this into the kit component's `productName` when building an order or quote kit line from this template — component names are stored as given and are not derived from product references. | | `kits.subKits` | KitSubAssembly[] | The kit's sub-assemblies, each holding its own component products. Nested one level deep (a sub-assembly cannot itself contain sub-assemblies). | | `kits.subKits.subKitId` | string | The sub-assembly's unique id. | | `kits.subKits.title` | string | The sub-assembly's display title. | | `kits.subKits.index` | integer | The sub-assembly's position in display order within the kit. | | `kits.subKits.components` | KitProductComponent[] | The component products that make up this sub-assembly. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/catalogue/kits/{kitId} **Service:** Catalogue **Operation:** `CatalogueService_GetKit` Returns the kit with its priced rows, top-level component products, and sub-assemblies — the single call that lets you drill into one kit's full detail. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `kitId` | path | string | yes | The id of the kit to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetKitResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No product kit with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetKitResponse` | Field | Type | Description | | --- | --- | --- | | `kit` | Kit | The requested kit, including its rows and full component tree. | | `kit.kitId` | string | The kit's id — the id of the catalogue product that fronts this kit. This is the id a kit line item references, and the id GetKit accepts. | | `kit.name` | string | The kit's display name. | | `kit.rows` | KitRow[] | The kit's priced variant rows. Populated by GetKit; the kit listing leaves this empty. | | `kit.rows.kitRowId` | string | The kit variant row's unique id. | | `kit.rows.attributes` | RowAttribute[] | Attribute pairs that identify this kit variant. | | `kit.rows.markedUpPrice` | Money | The computed marked-up sell price, for reference. | | `kit.rows.prices` | RowPrice[] | The variant's price at each of your price levels. | | `kit.components` | KitProductComponent[] | The kit's top-level component products (those not inside a sub-assembly). Populated by GetKit; the kit listing leaves this empty. | | `kit.components.kitProductId` | string | The component's unique id within the kit. | | `kit.components.productId` | string | The id of the catalogue product used as this component. Additional-product components only. | | `kit.components.productRowId` | string | The id of the specific product variant row used as this component. Catalogue-product components only. | | `kit.components.colour` | string | The component's colour. Catalogue-product components only. | | `kit.components.material` | string | The component's material. Catalogue-product components only. | | `kit.components.quantity` | string | The component's quantity, as a decimal string. | | `kit.components.productType` | KitComponentType | The component's kind: catalogue product, notes, or labour. Determines which of the fields below are populated. On-the-fly is never a kit-template component, so it is not part of this set. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `kit.components.notes` | string | Free-text note. Populated when this is a notes component. | | `kit.components.notesType` | NotesType | Whether the note is internal-only or customer-visible. Notes components only. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `kit.components.labourUserId` | string | The assigned company user's id. Populated when this is a labour component. | | `kit.components.hourlyRateCharged` | Money | The labour charge-out rate per hour. Labour components only. | | `kit.components.hourlyRateCost` | Money | The labour cost rate per hour. Labour components only. | | `kit.components.customHourlyRateCharged` | Money | A custom override of the charge-out rate, when set. Labour components only. | | `kit.components.customHourlyRateCost` | Money | A custom override of the cost rate, when set. Labour components only. | | `kit.components.productName` | string | The display name of the catalogue product used as this component. Catalogue-product components only. Echo this into the kit component's `productName` when building an order or quote kit line from this template — component names are stored as given and are not derived from product references. | | `kit.subKits` | KitSubAssembly[] | The kit's sub-assemblies, each holding its own component products. Nested one level deep (a sub-assembly cannot itself contain sub-assemblies). | | `kit.subKits.subKitId` | string | The sub-assembly's unique id. | | `kit.subKits.title` | string | The sub-assembly's display title. | | `kit.subKits.index` | integer | The sub-assembly's position in display order within the kit. | | `kit.subKits.components` | KitProductComponent[] | The component products that make up this sub-assembly. | --- # GET /v1/catalogue/products **Service:** Catalogue **Operation:** `CatalogueService_ListProducts` Returns a paginated list of catalogue product summaries. The listing is lightweight: `rows` is not populated — retrieve a product with GetProduct to get its priced variant rows. Product kits are not included here; list them with ListKits. Products are returned alphabetically by name. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of products to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `name` | query | string | no | Optional: only return products whose name contains this text. A browse aid, not a precise lookup key. | | `categoryName` | query | string | no | Optional: only return products in this category, by category name. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListProductsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListProductsResponse` | Field | Type | Description | | --- | --- | --- | | `products` | Product[] | The products in this page. | | `products.productId` | string | The product's unique id. Use this to reference the product on a line item. | | `products.name` | string | The product's display name. Not guaranteed unique; not a lookup key. | | `products.categoryId` | string | The id of the category this product belongs to. | | `products.categoryName` | string | The display name of the product's category. | | `products.pricingStrategy` | PricingStrategy | How this product's quantity is interpreted for pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `products.isTaxFree` | boolean | Whether this product is exempt from tax. | | `products.rows` | ProductRow[] | The product's priced variant rows. | | `products.rows.productRowId` | string | The variant row's unique id. Reference this on a catalogue line item. | | `products.rows.colour` | string | The variant's own colour. Usually empty — colour is normally chosen per line from `colourOptions` rather than baked into the row. | | `products.rows.thickness` | string | The variant's thickness. | | `products.rows.attributes` | RowAttribute[] | Additional attribute pairs that identify this variant, e.g. [{name:"Size", value:"A4"}]. | | `products.rows.markedUpPrice` | Money | The computed marked-up sell price, for reference. | | `products.rows.prices` | RowPrice[] | The variant's price at each of your price levels. | | `products.rows.colourOptions` | string[] | The colours a catalogue line built on this row may use, when the product's material offers a colour selection. A line's colour must be one of these. They come from the row's material, so every row sharing that material offers the same colours; the list is empty when the product has no colour choice. Colour is the per-line choice — the attributes above identify the variant. | | `products.rows.cost` | Money | What this variant costs you, as recorded on the catalogue row: the figure `markedUpPrice` is marked up from. Your own data, never shown to customers. Zero when no cost is recorded. | | `products.rows.markup` | string | The markup applied to `cost` to reach `markedUpPrice`, as a decimal string: a percentage ("25" is 25%) when `markupIsPercentage` is true, otherwise a fixed amount in the currency's major unit. | | `products.rows.markupIsPercentage` | boolean | Whether `markup` is a percentage of `cost` (true) or a fixed amount. | | `products.rows.materialId` | string | The id of the material this variant is priced under. A product with more than one material carries a separate set of rows per material, so two rows with the same attributes are told apart by this. Empty when the row has no material. | | `products.rows.materialName` | string | The display name of that material, for example "Colorbond". Empty when `materialId` is. | | `products.isInventoryTracked` | boolean | Whether this product participates in inventory tracking. When true, stock levels for its rows are available from the Inventory API. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/catalogue/products/{productId} **Service:** Catalogue **Operation:** `CatalogueService_GetProduct` Returns the full product, including all of its priced variant rows. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `productId` | path | string | yes | The id of the product to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetProductResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No catalogue product with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetProductResponse` | Field | Type | Description | | --- | --- | --- | | `product` | Product | The requested product. | | `product.productId` | string | The product's unique id. Use this to reference the product on a line item. | | `product.name` | string | The product's display name. Not guaranteed unique; not a lookup key. | | `product.categoryId` | string | The id of the category this product belongs to. | | `product.categoryName` | string | The display name of the product's category. | | `product.pricingStrategy` | PricingStrategy | How this product's quantity is interpreted for pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `product.isTaxFree` | boolean | Whether this product is exempt from tax. | | `product.rows` | ProductRow[] | The product's priced variant rows. | | `product.rows.productRowId` | string | The variant row's unique id. Reference this on a catalogue line item. | | `product.rows.colour` | string | The variant's own colour. Usually empty — colour is normally chosen per line from `colourOptions` rather than baked into the row. | | `product.rows.thickness` | string | The variant's thickness. | | `product.rows.attributes` | RowAttribute[] | Additional attribute pairs that identify this variant, e.g. [{name:"Size", value:"A4"}]. | | `product.rows.markedUpPrice` | Money | The computed marked-up sell price, for reference. | | `product.rows.prices` | RowPrice[] | The variant's price at each of your price levels. | | `product.rows.colourOptions` | string[] | The colours a catalogue line built on this row may use, when the product's material offers a colour selection. A line's colour must be one of these. They come from the row's material, so every row sharing that material offers the same colours; the list is empty when the product has no colour choice. Colour is the per-line choice — the attributes above identify the variant. | | `product.rows.cost` | Money | What this variant costs you, as recorded on the catalogue row: the figure `markedUpPrice` is marked up from. Your own data, never shown to customers. Zero when no cost is recorded. | | `product.rows.markup` | string | The markup applied to `cost` to reach `markedUpPrice`, as a decimal string: a percentage ("25" is 25%) when `markupIsPercentage` is true, otherwise a fixed amount in the currency's major unit. | | `product.rows.markupIsPercentage` | boolean | Whether `markup` is a percentage of `cost` (true) or a fixed amount. | | `product.rows.materialId` | string | The id of the material this variant is priced under. A product with more than one material carries a separate set of rows per material, so two rows with the same attributes are told apart by this. Empty when the row has no material. | | `product.rows.materialName` | string | The display name of that material, for example "Colorbond". Empty when `materialId` is. | | `product.isInventoryTracked` | boolean | Whether this product participates in inventory tracking. When true, stock levels for its rows are available from the Inventory API. | --- # GET /v1/catalogue:search **Service:** Search **Operation:** `SearchService_SearchCatalogue` Searches products, kits, and flashings by text query and returns one ranked list of minimal hits, most relevant first. Every whitespace- separated word in the query must match — against an entry's name, its category, a material, or a product attribute value (for example an Item Code). Fetch full detail with GetProduct, GetKit, or GetFlashing using the hit's id. Result order is relevance-based and may change between requests; it is not a stable contract. This operation is in preview and not yet covered by the compatibility guarantees (see Versioning in the API conventions). Known limitations today: a page-by-page walk of a large result set can drop or repeat a row between pages, so fetch one large page when completeness matters; and a flashing hit's id cannot yet be passed to GetFlashing (see the hit's id field). ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `query` | query | string | no | The text to search for. Every whitespace-separated word must match. | | `pageSize` | query | integer | no | Maximum number of hits to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. A token is bound to the query it was minted for. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `SearchCatalogueResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an empty query or an unusable page token. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | Authentication failed: the bearer token is missing, invalid, or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `SearchCatalogueResponse` | Field | Type | Description | | --- | --- | --- | | `hits` | CatalogueSearchHit[] | The hits in this page. | | `hits.id` | string | The entry's unique id. The prefix always agrees with `type`. A `prod_` id drills in with GetProduct and a `kit_` id with GetKit. KNOWN LIMITATION: a `flashing_` id is the flashing-family id and is NOT currently accepted by GetFlashing (which takes the fronting `flashtpl_` template id); flashing hits are therefore not drillable yet. Treat a flashing hit as name-only for now. | | `hits.type` | CatalogueEntryType | What kind of catalogue entry this hit refers to. The kind of catalogue entry a search hit refers to. One of: CATALOGUE_ENTRY_TYPE_UNSPECIFIED, CATALOGUE_ENTRY_TYPE_PRODUCT, CATALOGUE_ENTRY_TYPE_KIT, CATALOGUE_ENTRY_TYPE_FLASHING. | | `hits.name` | string | The entry's display name. | | `hits.categoryId` | string | The id of the entry's category. Unset for flashings. | | `hits.categoryName` | string | The display name of the entry's category. Unset for flashings. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/company **Service:** Company **Operation:** `CompanyService_GetCompany` A singleton read keyed by the bearer token. Returns the company's identity, locale and currency settings, the measurement system its quantities and dimensions are expressed in, its tax context, contact and address details, and logo. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetCompanyResponse` | A successful response. | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetCompanyResponse` | Field | Type | Description | | --- | --- | --- | | `company` | Company | Your company's settings. | | `company.companyId` | string | The company's unique id. | | `company.name` | string | The company's display name. | | `company.domain` | string | The company's Factory domain. | | `company.countryCode` | string | The company's country code, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Empty when not set. | | `company.timeZone` | string | The company's IANA time zone name (e.g. "Australia/Sydney"). | | `company.currency` | string | The ISO 4217 currency code all monetary amounts use. Currently one of "AUD", "CAD", "NZD", "USD", "GBP", or "ZAR"; the set may grow. | | `company.currencySymbol` | string | The currency's display symbol (e.g. "$"). | | `company.measurementSystem` | MeasurementSystem | The measurement system quantities and dimensions are expressed in. The measurement system a company's quantities are expressed in. - MEASUREMENT_SYSTEM_BOTH: The company works in both systems; each document line's pricing strategy declares its own unit. One of: MEASUREMENT_SYSTEM_UNSPECIFIED, MEASUREMENT_SYSTEM_METRIC, MEASUREMENT_SYSTEM_IMPERIAL, MEASUREMENT_SYSTEM_BOTH. | | `company.taxRate` | string | The tax rate applied to the company's sales documents, as a decimal fraction (e.g. "0.10" means 10%). The server computes document tax with this rate; it cannot be set per document. | | `company.taxIdentifier` | TaxIdentifier | The company's tax identifier, if set. On read the scheme is ABN for Australian accounts. | | `company.taxIdentifier.value` | string | The identifier value, e.g. an 11-digit ABN. Its format depends on `type` and is NOT validated in v1: an ABN is accepted as any non-empty string of up to 15 characters. The length cap may widen when more schemes are supported. | | `company.taxIdentifier.type` | TaxIdentifierType | Which tax / business registration scheme `value` belongs to. Must be a specified scheme when a TaxIdentifier is present. v1 accepts only ABN. The tax / business registration scheme a TaxIdentifier belongs to. v1 accepts only ABN; the remaining values are reserved for future non-AU support. - TAX_IDENTIFIER_TYPE_UNSPECIFIED: Default, unset value. Not a valid scheme when a TaxIdentifier is supplied. - TAX_IDENTIFIER_TYPE_ABN: Australian Business Number. The only scheme accepted in v1. - TAX_IDENTIFIER_TYPE_EU_VAT: EU VAT number. Does not cover the United Kingdom — a UK VAT registration is TAX_IDENTIFIER_TYPE_GB_VAT. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_EIN: US Employer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_TIN: US Taxpayer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_SSN: US Social Security Number (used by sole traders). Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_GB_VAT: United Kingdom VAT registration number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_NZ_GST: New Zealand GST number (the IRD number of a GST-registered business). Reserved — not accepted in v1. One of: TAX_IDENTIFIER_TYPE_UNSPECIFIED, TAX_IDENTIFIER_TYPE_ABN, TAX_IDENTIFIER_TYPE_EU_VAT, TAX_IDENTIFIER_TYPE_US_EIN, TAX_IDENTIFIER_TYPE_US_TIN, TAX_IDENTIFIER_TYPE_US_SSN, TAX_IDENTIFIER_TYPE_GB_VAT, TAX_IDENTIFIER_TYPE_NZ_GST. | | `company.email` | string | The company's contact email address. | | `company.phone` | string | The company's contact phone number. | | `company.website` | string | The company's website URL. | | `company.address` | string | The company's street address (first line). | | `company.address2` | string | The company's street address (second line). | | `company.city` | string | The company's city or suburb. | | `company.state` | string | The company's state or region code (e.g. "VIC"). | | `company.postalCode` | string | The company's postal code (ZIP code, postcode). | | `company.logoUrl` | string | A URL to the company's logo image, if one is set. Served from Factory's media CDN; the URL is stable and safe to cache. | | `company.pricingSettings` | PricingSettings | The account settings that affect how lines are priced. Always present on reads. | | `company.pricingSettings.minimumLinealMetreLengthMm` | string | The minimum length charged on lines priced per lineal metre, in millimetres, as a decimal string (for example "1000"). Each measured piece shorter than this is priced as this length. Empty when not enabled. | | `company.pricingSettings.minimumLinealFeetLengthMm` | string | The minimum length charged on lines priced per lineal foot, in millimetres, as a decimal string (for example "304.8", one foot). Each measured piece shorter than this is priced as this length. Empty when not enabled. | | `company.pricingSettings.minimumFlashingLengthMm` | string | The minimum length charged on each piece of a flashing, in millimetres, as a decimal string. Factory prices a flashing piece shorter than this as this length; the API itself never prices flashings, so apply it when you compute a flashing line's `totalPrice`. Empty when not enabled. | | `company.pricingSettings.crushFoldBendCount` | integer | The number of bends a crush fold counts as when a flashing's `bends` are counted. 0 means the default of 2. | --- # GET /v1/company/custom-fields **Service:** Company **Operation:** `CompanyService_ListCompanyCustomFields` Custom fields are account-configured, typed fields on sales documents, customers, and suppliers. The `key` returned here is the identifier write endpoints accept — for example `customFields` on CreateQuoteRequest is an object keyed by these keys. For select and multi-select fields, the value a write must carry is an option `key` from the definition's options (not the option's label). Hidden definitions are included — existing documents can still carry values for fields that were hidden after being set. Optionally filter by module. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `module` | query | string | no | Only return definitions for this module. Optional; unspecified returns every module's definitions. One of: CUSTOM_FIELD_MODULE_UNSPECIFIED, CUSTOM_FIELD_MODULE_SALES_DOCUMENTS, CUSTOM_FIELD_MODULE_CUSTOMERS, CUSTOM_FIELD_MODULE_SUPPLIERS, CUSTOM_FIELD_MODULE_PURCHASE_ORDERS. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListCompanyCustomFieldsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unknown module filter value. The body is a validation-failure envelope. | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListCompanyCustomFieldsResponse` | Field | Type | Description | | --- | --- | --- | | `customFields` | CustomFieldDefinition[] | The company's custom field definitions. | | `customFields.key` | string | The field's key — the identifier write endpoints accept. | | `customFields.name` | string | The field's display name. | | `customFields.module` | CustomFieldModule | Which record type the field is defined for. The record type a custom field is defined for. - CUSTOM_FIELD_MODULE_SALES_DOCUMENTS: Quotes and orders share one set of sales-document fields. One of: CUSTOM_FIELD_MODULE_UNSPECIFIED, CUSTOM_FIELD_MODULE_SALES_DOCUMENTS, CUSTOM_FIELD_MODULE_CUSTOMERS, CUSTOM_FIELD_MODULE_SUPPLIERS, CUSTOM_FIELD_MODULE_PURCHASE_ORDERS. | | `customFields.type` | CustomFieldType | The field's value type. A custom field's value type. One of: CUSTOM_FIELD_TYPE_UNSPECIFIED, CUSTOM_FIELD_TYPE_TEXT, CUSTOM_FIELD_TYPE_NUMBER, CUSTOM_FIELD_TYPE_CURRENCY, CUSTOM_FIELD_TYPE_DATE, CUSTOM_FIELD_TYPE_DATETIME, CUSTOM_FIELD_TYPE_SELECT, CUSTOM_FIELD_TYPE_CHECKBOX. | | `customFields.options` | CustomFieldOption[] | The permitted choices, for select and multi-select fields. A written value must be an option `key` (multi-select: a list of option keys). | | `customFields.options.key` | string | The option's key — what a written value must contain. | | `customFields.options.label` | string | The option's display label. | | `customFields.options.isDefault` | boolean | Whether this option is the field's default. | | `customFields.readOnly` | boolean | The field's value is computed and cannot be written. | | `customFields.isHidden` | boolean | Whether the field is hidden in the Factory app. Hidden fields are still returned here so values on existing documents can be interpreted. | --- # GET /v1/company/labels **Service:** Labels **Operation:** `LabelService_ListLabels` Returns the labels configured for the authenticated company. Use the returned label ids when replacing an order's labels with SetOrderLabels. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | The maximum number of labels to return in one page. Optional; a server default applies when unset. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListLabelsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListLabelsResponse` | Field | Type | Description | | --- | --- | --- | | `labels` | Label[] | One page of labels, sorted by name. | | `labels.labelId` | string | The label's id. | | `labels.name` | string | Display name of the label. Unique within your company. | | `labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/company/order-statuses **Service:** Order statuses **Operation:** `OrderStatusService_ListOrderStatuses` Returns the order statuses (workflow columns) configured for the authenticated company, in display order. Use the returned status ids when setting an order's status on CreateOrder. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of statuses to return per page. 0 uses the server default. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListOrderStatusesResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListOrderStatusesResponse` | Field | Type | Description | | --- | --- | --- | | `statuses` | OrderStatus[] | The order statuses in this page, in display order. | | `statuses.statusId` | string | The status's unique id. Pass this as `statusId` on CreateOrder. | | `statuses.name` | string | The display name of the status (e.g. "Submitted"). | | `statuses.index` | integer | The status's position in the workflow, used for display ordering. | | `statuses.colour` | string | The status's display colour. | | `statuses.isDefault` | boolean | Whether this is the default status applied when an order is submitted without an explicit status. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/customers **Service:** Customers **Operation:** `CustomerService_ListCustomers` A company-scoped list of customers, optionally filtered by company name (a case-insensitive contains-match). Use it to resolve a customer by name before referencing it on an order — compare each hit's full `companyName` with the name you hold, since a single hit can still be a partial match. Customers are returned alphabetically by company name. List entries are summaries — id, company name, billing address, and last order time; fetch the full record (email, contacts, tax identifier, price level) with GetCustomer. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of customers to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `companyName` | query | string | no | Optional: only return customers whose company name contains this text, compared case-insensitively — "Apex" and "ex In" both match "Apex Industrial Solutions". To resolve a customer by name, compare each hit's `companyName` with the full name before taking its id; a single hit is not proof of an exact match. Pages are filtered after they are fetched, so a page may hold fewer than `pageSize` entries while `nextPageToken` is still set. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListCustomersResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListCustomersResponse` | Field | Type | Description | | --- | --- | --- | | `customers` | Customer[] | The customers in this page. | | `customers.customerId` | string | The customer's unique id. | | `customers.companyName` | string | The customer's company name. Unique within your company and matched case-insensitively when resolving. | | `customers.taxIdentifier` | TaxIdentifier | The customer's tax identifier, if set. Not required to be unique. On read the scheme is ABN for existing (Australian) customers. | | `customers.taxIdentifier.value` | string | The identifier value, e.g. an 11-digit ABN. Its format depends on `type` and is NOT validated in v1: an ABN is accepted as any non-empty string of up to 15 characters. The length cap may widen when more schemes are supported. | | `customers.taxIdentifier.type` | TaxIdentifierType | Which tax / business registration scheme `value` belongs to. Must be a specified scheme when a TaxIdentifier is present. v1 accepts only ABN. The tax / business registration scheme a TaxIdentifier belongs to. v1 accepts only ABN; the remaining values are reserved for future non-AU support. - TAX_IDENTIFIER_TYPE_UNSPECIFIED: Default, unset value. Not a valid scheme when a TaxIdentifier is supplied. - TAX_IDENTIFIER_TYPE_ABN: Australian Business Number. The only scheme accepted in v1. - TAX_IDENTIFIER_TYPE_EU_VAT: EU VAT number. Does not cover the United Kingdom — a UK VAT registration is TAX_IDENTIFIER_TYPE_GB_VAT. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_EIN: US Employer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_TIN: US Taxpayer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_SSN: US Social Security Number (used by sole traders). Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_GB_VAT: United Kingdom VAT registration number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_NZ_GST: New Zealand GST number (the IRD number of a GST-registered business). Reserved — not accepted in v1. One of: TAX_IDENTIFIER_TYPE_UNSPECIFIED, TAX_IDENTIFIER_TYPE_ABN, TAX_IDENTIFIER_TYPE_EU_VAT, TAX_IDENTIFIER_TYPE_US_EIN, TAX_IDENTIFIER_TYPE_US_TIN, TAX_IDENTIFIER_TYPE_US_SSN, TAX_IDENTIFIER_TYPE_GB_VAT, TAX_IDENTIFIER_TYPE_NZ_GST. | | `customers.email` | string | The customer's email address, if set. | | `customers.phone` | string | The customer's phone number. | | `customers.defaultPriceLevelId` | string | The customer's default price level, by id. Read reference only; line pricing is supplied by the caller on each order line. | | `customers.defaultPriceLevelName` | string | The name of the customer's default price level. | | `customers.billingAddress` | Address | The customer's billing address. | | `customers.billingAddress.address1` | string | First address line (street number and name). | | `customers.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `customers.billingAddress.city` | string | City or suburb. | | `customers.billingAddress.state` | string | State, province, or region. | | `customers.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `customers.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `customers.deliveryAddresses` | Address[] | The customer's delivery addresses. | | `customers.deliveryAddresses.address1` | string | First address line (street number and name). | | `customers.deliveryAddresses.address2` | string | Second address line (unit, suite, or similar). | | `customers.deliveryAddresses.city` | string | City or suburb. | | `customers.deliveryAddresses.state` | string | State, province, or region. | | `customers.deliveryAddresses.postalCode` | string | Postal code (ZIP code, postcode). | | `customers.deliveryAddresses.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `customers.contacts` | CustomerContact[] | The customer's contacts. | | `customers.contacts.contactId` | string | The contact's unique id. | | `customers.contacts.name` | string | The contact's name. | | `customers.contacts.email` | string | The contact's email address. | | `customers.contacts.phone` | string | The contact's phone number. | | `customers.contacts.mobile` | string | The contact's mobile number. | | `customers.isOnCreditHold` | boolean | Whether the customer is currently on credit hold. | | `customers.disableTaxByDefault` | boolean | Whether this customer's lines are tax-free by default: a line written for the customer without `isTaxFree` is exempt when this is true, and a line's own `isTaxFree` always takes precedence. Set in Factory under the customer's settings; CreateCustomer does not accept it in this version. | | `customers.lastOrderAt` | string | When the customer most recently placed an order. Populated on ListCustomers entries; not currently returned by GetCustomer. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/customers **Service:** Customers **Operation:** `CustomerService_CreateCustomer` Adds a new customer — company details, addresses, and contacts — to your company. Company name and email are required; a tax identifier is optional (in v1 that means an ABN). The company name must be unique within your company (matched case-insensitively); a duplicate is rejected with HTTP 400 (validation_failure). To make a retry safe, send an idempotency key in `requestId`; a repeat with the same key can never create a second customer — it is rejected with HTTP 409 carrying the original customer's id. Reference the created customer by its id when creating an order or quote. ## Request body `CreateCustomerRequest` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `companyName` | string | yes | The customer's company name. Required, and must be unique within your company (matched case-insensitively); a duplicate is rejected. | | `taxIdentifier` | TaxIdentifier | no | The customer's tax identifier. Optional. In v1 the only accepted scheme is ABN (Australian Business Number); supplying any other scheme is rejected. | | `taxIdentifier.value` | string | no | The identifier value, e.g. an 11-digit ABN. Its format depends on `type` and is NOT validated in v1: an ABN is accepted as any non-empty string of up to 15 characters. The length cap may widen when more schemes are supported. | | `taxIdentifier.type` | TaxIdentifierType | no | Which tax / business registration scheme `value` belongs to. Must be a specified scheme when a TaxIdentifier is present. v1 accepts only ABN. The tax / business registration scheme a TaxIdentifier belongs to. v1 accepts only ABN; the remaining values are reserved for future non-AU support. - TAX_IDENTIFIER_TYPE_UNSPECIFIED: Default, unset value. Not a valid scheme when a TaxIdentifier is supplied. - TAX_IDENTIFIER_TYPE_ABN: Australian Business Number. The only scheme accepted in v1. - TAX_IDENTIFIER_TYPE_EU_VAT: EU VAT number. Does not cover the United Kingdom — a UK VAT registration is TAX_IDENTIFIER_TYPE_GB_VAT. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_EIN: US Employer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_TIN: US Taxpayer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_SSN: US Social Security Number (used by sole traders). Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_GB_VAT: United Kingdom VAT registration number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_NZ_GST: New Zealand GST number (the IRD number of a GST-registered business). Reserved — not accepted in v1. One of: TAX_IDENTIFIER_TYPE_UNSPECIFIED, TAX_IDENTIFIER_TYPE_ABN, TAX_IDENTIFIER_TYPE_EU_VAT, TAX_IDENTIFIER_TYPE_US_EIN, TAX_IDENTIFIER_TYPE_US_TIN, TAX_IDENTIFIER_TYPE_US_SSN, TAX_IDENTIFIER_TYPE_GB_VAT, TAX_IDENTIFIER_TYPE_NZ_GST. | | `email` | string | yes | The customer's email address. Required; must be a valid email address. | | `phone` | string | no | The customer's phone number. | | `billingAddress` | Address | no | The customer's billing address. | | `billingAddress.address1` | string | no | First address line (street number and name). | | `billingAddress.address2` | string | no | Second address line (unit, suite, or similar). | | `billingAddress.city` | string | no | City or suburb. | | `billingAddress.state` | string | no | State, province, or region. | | `billingAddress.postalCode` | string | no | Postal code (ZIP code, postcode). | | `billingAddress.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `deliveryAddresses` | Address[] | no | The customer's delivery addresses. | | `deliveryAddresses.address1` | string | no | First address line (street number and name). | | `deliveryAddresses.address2` | string | no | Second address line (unit, suite, or similar). | | `deliveryAddresses.city` | string | no | City or suburb. | | `deliveryAddresses.state` | string | no | State, province, or region. | | `deliveryAddresses.postalCode` | string | no | Postal code (ZIP code, postcode). | | `deliveryAddresses.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `contacts` | CustomerContact[] | no | The customer's contacts, at most 50 on a create. Each contact's name is required; its id is assigned by the server, so omit it on create. | | `contacts.contactId` | string | no | The contact's unique id. | | `contacts.name` | string | no | The contact's name. | | `contacts.email` | string | no | The contact's email address. | | `contacts.phone` | string | no | The contact's phone number. | | `contacts.mobile` | string | no | The contact's mobile number. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `CreateCustomerResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation (for example a missing required field or a duplicate company name). The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the customer it created), its outcome is still unknown, or the key was reused with a materially different body. Verify the existing customer with ListCustomers (filter by `companyName` and compare the full name), then send a new `requestId` to create a new customer. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `CreateCustomerResponse` | Field | Type | Description | | --- | --- | --- | | `customer` | Customer | The created customer. | | `customer.customerId` | string | The customer's unique id. | | `customer.companyName` | string | The customer's company name. Unique within your company and matched case-insensitively when resolving. | | `customer.taxIdentifier` | TaxIdentifier | The customer's tax identifier, if set. Not required to be unique. On read the scheme is ABN for existing (Australian) customers. | | `customer.taxIdentifier.value` | string | The identifier value, e.g. an 11-digit ABN. Its format depends on `type` and is NOT validated in v1: an ABN is accepted as any non-empty string of up to 15 characters. The length cap may widen when more schemes are supported. | | `customer.taxIdentifier.type` | TaxIdentifierType | Which tax / business registration scheme `value` belongs to. Must be a specified scheme when a TaxIdentifier is present. v1 accepts only ABN. The tax / business registration scheme a TaxIdentifier belongs to. v1 accepts only ABN; the remaining values are reserved for future non-AU support. - TAX_IDENTIFIER_TYPE_UNSPECIFIED: Default, unset value. Not a valid scheme when a TaxIdentifier is supplied. - TAX_IDENTIFIER_TYPE_ABN: Australian Business Number. The only scheme accepted in v1. - TAX_IDENTIFIER_TYPE_EU_VAT: EU VAT number. Does not cover the United Kingdom — a UK VAT registration is TAX_IDENTIFIER_TYPE_GB_VAT. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_EIN: US Employer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_TIN: US Taxpayer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_SSN: US Social Security Number (used by sole traders). Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_GB_VAT: United Kingdom VAT registration number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_NZ_GST: New Zealand GST number (the IRD number of a GST-registered business). Reserved — not accepted in v1. One of: TAX_IDENTIFIER_TYPE_UNSPECIFIED, TAX_IDENTIFIER_TYPE_ABN, TAX_IDENTIFIER_TYPE_EU_VAT, TAX_IDENTIFIER_TYPE_US_EIN, TAX_IDENTIFIER_TYPE_US_TIN, TAX_IDENTIFIER_TYPE_US_SSN, TAX_IDENTIFIER_TYPE_GB_VAT, TAX_IDENTIFIER_TYPE_NZ_GST. | | `customer.email` | string | The customer's email address, if set. | | `customer.phone` | string | The customer's phone number. | | `customer.defaultPriceLevelId` | string | The customer's default price level, by id. Read reference only; line pricing is supplied by the caller on each order line. | | `customer.defaultPriceLevelName` | string | The name of the customer's default price level. | | `customer.billingAddress` | Address | The customer's billing address. | | `customer.billingAddress.address1` | string | First address line (street number and name). | | `customer.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `customer.billingAddress.city` | string | City or suburb. | | `customer.billingAddress.state` | string | State, province, or region. | | `customer.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `customer.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `customer.deliveryAddresses` | Address[] | The customer's delivery addresses. | | `customer.deliveryAddresses.address1` | string | First address line (street number and name). | | `customer.deliveryAddresses.address2` | string | Second address line (unit, suite, or similar). | | `customer.deliveryAddresses.city` | string | City or suburb. | | `customer.deliveryAddresses.state` | string | State, province, or region. | | `customer.deliveryAddresses.postalCode` | string | Postal code (ZIP code, postcode). | | `customer.deliveryAddresses.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `customer.contacts` | CustomerContact[] | The customer's contacts. | | `customer.contacts.contactId` | string | The contact's unique id. | | `customer.contacts.name` | string | The contact's name. | | `customer.contacts.email` | string | The contact's email address. | | `customer.contacts.phone` | string | The contact's phone number. | | `customer.contacts.mobile` | string | The contact's mobile number. | | `customer.isOnCreditHold` | boolean | Whether the customer is currently on credit hold. | | `customer.disableTaxByDefault` | boolean | Whether this customer's lines are tax-free by default: a line written for the customer without `isTaxFree` is exempt when this is true, and a line's own `isTaxFree` always takes precedence. Set in Factory under the customer's settings; CreateCustomer does not accept it in this version. | | `customer.lastOrderAt` | string | When the customer most recently placed an order. Populated on ListCustomers entries; not currently returned by GetCustomer. | --- # GET /v1/customers/{customerId} **Service:** Customers **Operation:** `CustomerService_GetCustomer` Returns the full customer record — company details, addresses, contacts, and account flags — for the given id, scoped to your company. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `customerId` | path | string | yes | The id of the customer to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetCustomerResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No customer with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetCustomerResponse` | Field | Type | Description | | --- | --- | --- | | `customer` | Customer | The requested customer. | | `customer.customerId` | string | The customer's unique id. | | `customer.companyName` | string | The customer's company name. Unique within your company and matched case-insensitively when resolving. | | `customer.taxIdentifier` | TaxIdentifier | The customer's tax identifier, if set. Not required to be unique. On read the scheme is ABN for existing (Australian) customers. | | `customer.taxIdentifier.value` | string | The identifier value, e.g. an 11-digit ABN. Its format depends on `type` and is NOT validated in v1: an ABN is accepted as any non-empty string of up to 15 characters. The length cap may widen when more schemes are supported. | | `customer.taxIdentifier.type` | TaxIdentifierType | Which tax / business registration scheme `value` belongs to. Must be a specified scheme when a TaxIdentifier is present. v1 accepts only ABN. The tax / business registration scheme a TaxIdentifier belongs to. v1 accepts only ABN; the remaining values are reserved for future non-AU support. - TAX_IDENTIFIER_TYPE_UNSPECIFIED: Default, unset value. Not a valid scheme when a TaxIdentifier is supplied. - TAX_IDENTIFIER_TYPE_ABN: Australian Business Number. The only scheme accepted in v1. - TAX_IDENTIFIER_TYPE_EU_VAT: EU VAT number. Does not cover the United Kingdom — a UK VAT registration is TAX_IDENTIFIER_TYPE_GB_VAT. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_EIN: US Employer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_TIN: US Taxpayer Identification Number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_US_SSN: US Social Security Number (used by sole traders). Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_GB_VAT: United Kingdom VAT registration number. Reserved — not accepted in v1. - TAX_IDENTIFIER_TYPE_NZ_GST: New Zealand GST number (the IRD number of a GST-registered business). Reserved — not accepted in v1. One of: TAX_IDENTIFIER_TYPE_UNSPECIFIED, TAX_IDENTIFIER_TYPE_ABN, TAX_IDENTIFIER_TYPE_EU_VAT, TAX_IDENTIFIER_TYPE_US_EIN, TAX_IDENTIFIER_TYPE_US_TIN, TAX_IDENTIFIER_TYPE_US_SSN, TAX_IDENTIFIER_TYPE_GB_VAT, TAX_IDENTIFIER_TYPE_NZ_GST. | | `customer.email` | string | The customer's email address, if set. | | `customer.phone` | string | The customer's phone number. | | `customer.defaultPriceLevelId` | string | The customer's default price level, by id. Read reference only; line pricing is supplied by the caller on each order line. | | `customer.defaultPriceLevelName` | string | The name of the customer's default price level. | | `customer.billingAddress` | Address | The customer's billing address. | | `customer.billingAddress.address1` | string | First address line (street number and name). | | `customer.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `customer.billingAddress.city` | string | City or suburb. | | `customer.billingAddress.state` | string | State, province, or region. | | `customer.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `customer.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `customer.deliveryAddresses` | Address[] | The customer's delivery addresses. | | `customer.deliveryAddresses.address1` | string | First address line (street number and name). | | `customer.deliveryAddresses.address2` | string | Second address line (unit, suite, or similar). | | `customer.deliveryAddresses.city` | string | City or suburb. | | `customer.deliveryAddresses.state` | string | State, province, or region. | | `customer.deliveryAddresses.postalCode` | string | Postal code (ZIP code, postcode). | | `customer.deliveryAddresses.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `customer.contacts` | CustomerContact[] | The customer's contacts. | | `customer.contacts.contactId` | string | The contact's unique id. | | `customer.contacts.name` | string | The contact's name. | | `customer.contacts.email` | string | The contact's email address. | | `customer.contacts.phone` | string | The contact's phone number. | | `customer.contacts.mobile` | string | The contact's mobile number. | | `customer.isOnCreditHold` | boolean | Whether the customer is currently on credit hold. | | `customer.disableTaxByDefault` | boolean | Whether this customer's lines are tax-free by default: a line written for the customer without `isTaxFree` is exempt when this is true, and a line's own `isTaxFree` always takes precedence. Set in Factory under the customer's settings; CreateCustomer does not accept it in this version. | | `customer.lastOrderAt` | string | When the customer most recently placed an order. Populated on ListCustomers entries; not currently returned by GetCustomer. | --- # GET /v1/inventory **Service:** Inventory **Operation:** `InventoryService_ListInventory` Returns a paginated list of stock entries — one per product row per colour — for every catalogue product that has inventory tracking enabled. Every entry is the stock of a single variant row, not a product total: a product with several rows appears once per row, and a single-row product yields one entry. Each entry carries its variant's identifying attributes (thickness and name/value pairs), so entries are tellable apart on their own; sum a product's entries when you need its overall position. Filterable by product name, category, and supplier. Products are ordered alphabetically by name; entries within a product follow the catalogue's row order. Non-tracked products never appear. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of stock entries to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `name` | query | string | no | Optional: only return entries for products whose name contains this text (case-insensitive substring match). | | `categoryId` | query | string | no | Optional: only return entries for products in this category. | | `supplierId` | query | array | no | Optional: only return entries for products linked to any of these suppliers. A product appears if it is linked to at least one of the listed suppliers. Use ListSuppliers on the SupplierService to discover valid ids. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListInventoryResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListInventoryResponse` | Field | Type | Description | | --- | --- | --- | | `entries` | StockEntry[] | The stock entries in this page. | | `entries.productId` | string | The catalogue product this entry belongs to. | | `entries.productName` | string | The product's display name. | | `entries.productRowId` | string | The product variant row this entry is for — the same id the catalogue product's rows carry. Look the row up there when you need more than this entry shows, such as the variant's prices or colour options. | | `entries.colour` | string | The colour this entry is for. Empty when the product is not colour-tracked — in that case there is one entry per row. | | `entries.thickness` | string | The variant's thickness, when its catalogue row carries one. Empty otherwise. | | `entries.attributes` | RowAttribute[] | The attribute pairs identifying this variant, e.g. [{name:"Size", value:"A4"}] — the same attributes the catalogue product's row carries. Together with thickness and colour they label the entry; a single-row product has no attributes to distinguish, so the list may be empty. | | `entries.attributes.name` | string | The attribute name, e.g. "Size". | | `entries.attributes.value` | string | The attribute value, e.g. "A4". | | `entries.onHand` | string | Quantity currently in stock (on hand). The only directly maintained value; the others are derived from it and from order/purchase data. | | `entries.promised` | string | Quantity committed to submitted orders that have not yet been received by the customer. Computed from open order line items. | | `entries.onTheWay` | string | Quantity on submitted purchase orders that have not yet arrived. Computed from open purchase order items. | | `entries.available` | string | Quantity available for new orders: `onHand` minus promised. Can be negative when more stock is committed than is on hand. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/inventory/{productId} **Service:** Inventory **Operation:** `InventoryService_GetProductInventory` Returns every stock entry for the given product — one per row per colour. Each entry is the stock of a single variant row (and colour), not a product total; sum the entries when you need the product's overall position. Entries carry their variant's identifying attributes, and row ids match the rows returned by the catalogue GetProduct endpoint. The product must have inventory tracking enabled; a non-tracked product returns HTTP 404. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `productId` | path | string | yes | The id of the product to retrieve inventory for. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetProductInventoryResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No catalogue product with the given id exists for the authenticated company, or the product does not have inventory tracking enabled. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetProductInventoryResponse` | Field | Type | Description | | --- | --- | --- | | `productName` | string | The product's display name. | | `isColourTracked` | boolean | Whether the product is colour-tracked. When true, entries are split by colour; when false, there is one entry per row. | | `entries` | StockEntry[] | The stock entries for this product — one per row per colour. | | `entries.productId` | string | The catalogue product this entry belongs to. | | `entries.productName` | string | The product's display name. | | `entries.productRowId` | string | The product variant row this entry is for — the same id the catalogue product's rows carry. Look the row up there when you need more than this entry shows, such as the variant's prices or colour options. | | `entries.colour` | string | The colour this entry is for. Empty when the product is not colour-tracked — in that case there is one entry per row. | | `entries.thickness` | string | The variant's thickness, when its catalogue row carries one. Empty otherwise. | | `entries.attributes` | RowAttribute[] | The attribute pairs identifying this variant, e.g. [{name:"Size", value:"A4"}] — the same attributes the catalogue product's row carries. Together with thickness and colour they label the entry; a single-row product has no attributes to distinguish, so the list may be empty. | | `entries.attributes.name` | string | The attribute name, e.g. "Size". | | `entries.attributes.value` | string | The attribute value, e.g. "A4". | | `entries.onHand` | string | Quantity currently in stock (on hand). The only directly maintained value; the others are derived from it and from order/purchase data. | | `entries.promised` | string | Quantity committed to submitted orders that have not yet been received by the customer. Computed from open order line items. | | `entries.onTheWay` | string | Quantity on submitted purchase orders that have not yet arrived. Computed from open purchase order items. | | `entries.available` | string | Quantity available for new orders: `onHand` minus promised. Can be negative when more stock is committed than is on hand. | --- # PUT /v1/inventory/{productId} **Service:** Inventory **Operation:** `InventoryService_SetStockLevels` Replaces the on-hand quantity for each row+colour combination you include. Entries you omit are left unchanged — this is a partial update, not a full replace. The response echoes the product's complete stock position (all entries, including unchanged ones) with recomputed promised, on-the-way, and available. The product must have inventory tracking enabled; a non-tracked product returns HTTP 404. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `productId` | path | string | yes | The product to update. Required. | ## Request body `SetStockLevelsBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `entries` | StockLevelInput[] | yes | The stock levels to set. At least one entry is required. Each identifies a product row and optional colour, with the new on-hand quantity. Entries you omit are left unchanged. | | `entries.productRowId` | string | yes | The product variant row to set stock for. Must belong to the product identified in the request. | | `entries.colour` | string | no | The colour to set stock for. Required for colour-tracked products (one of the row's `colourOptions`); leave empty for non-colour-tracked products. | | `entries.onHand` | string | yes | The on-hand quantity to record, as a decimal string. Replaces the current value. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `SetStockLevelsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation — for example a row id that does not belong to this product, a colour not in the product's material, or a malformed `onHand` value. The body is a validation-failure envelope. | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No catalogue product with the given id exists for the authenticated company, or the product does not have inventory tracking enabled. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `SetStockLevelsResponse` | Field | Type | Description | | --- | --- | --- | | `entries` | StockEntry[] | The stock entries for the product after the update — all entries, including those not changed by this call, with recomputed promised, on-the-way, and available. | | `entries.productId` | string | The catalogue product this entry belongs to. | | `entries.productName` | string | The product's display name. | | `entries.productRowId` | string | The product variant row this entry is for — the same id the catalogue product's rows carry. Look the row up there when you need more than this entry shows, such as the variant's prices or colour options. | | `entries.colour` | string | The colour this entry is for. Empty when the product is not colour-tracked — in that case there is one entry per row. | | `entries.thickness` | string | The variant's thickness, when its catalogue row carries one. Empty otherwise. | | `entries.attributes` | RowAttribute[] | The attribute pairs identifying this variant, e.g. [{name:"Size", value:"A4"}] — the same attributes the catalogue product's row carries. Together with thickness and colour they label the entry; a single-row product has no attributes to distinguish, so the list may be empty. | | `entries.attributes.name` | string | The attribute name, e.g. "Size". | | `entries.attributes.value` | string | The attribute value, e.g. "A4". | | `entries.onHand` | string | Quantity currently in stock (on hand). The only directly maintained value; the others are derived from it and from order/purchase data. | | `entries.promised` | string | Quantity committed to submitted orders that have not yet been received by the customer. Computed from open order line items. | | `entries.onTheWay` | string | Quantity on submitted purchase orders that have not yet arrived. Computed from open purchase order items. | | `entries.available` | string | Quantity available for new orders: `onHand` minus promised. Can be negative when more stock is committed than is on hand. | --- # GET /v1/orders **Service:** Orders **Operation:** `OrderService_ListOrders` A flat, company-scoped list of orders. Confirmed orders only: quotes are read through QuoteService, and work-in-progress documents that have not yet become a quote or an order never appear here. Orders are returned most recently updated first; because that order shifts as orders change, use QueryOrders for a stable ordering suitable for incremental sync. Paginated via `pageSize` / `pageToken`. Orders carry three independent status dimensions, each filterable here: * Workflow status (`statusId` or `statusName`) — the order's position on your company's kanban board. Statuses are company-configured: discover yours with GET /v1/company/order-statuses, then filter by id or exact name. Filter by one, not both. * Payment status (`paymentStatus`) — whether the order is unpaid, partially paid, or paid. A fixed enum. * Received status (`receivedStatus`) — whether the order has been delivered, picked up, installed, or not yet received. A fixed enum. Additional filters: `customerId`, reference (substring), `isSubmitted` (draft/submitted), `labelIds`, `fulfilmentMethod`, `requiredAfter`/`requiredBefore` (required-by window), and archived (live vs archived view). ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of orders to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `customerId` | query | string | no | Optional: only return orders for this customer id. | | `reference` | query | string | no | Optional: only return orders whose external reference contains this text, compared case-insensitively (a substring match, not exact-equals) — e.g. to find a previously submitted order by part of its reference. | | `isSubmitted` | query | boolean | no | Optional: filter by whether the order has been submitted. Leave unset to return all orders; set to false to return only drafts; set to true to return only submitted orders. | | `archived` | query | boolean | no | Optional: return the archived set instead of the live one. False (the default) lists live orders, as always; true lists ONLY archived orders — a separate view, not a superset. Each returned order carries `isArchived`: true. Changing this flag mid-pagination invalidates the page token. | | `labelIds` | query | array | no | Optional: only return orders carrying at least one of these labels (by id — discover your labels with GET /v1/company/labels). An id not in your label set matches no orders. Combines with the other filters. Changing the label filter mid-pagination invalidates the page token. Duplicate ids are rejected. | | `statusId` | query | string | no | Optional: only return orders whose current workflow status matches this id. Workflow statuses are company-configured kanban columns — every company has different statuses. Call GET /v1/company/order-statuses to discover your company's statuses and their ids. Use either `statusId` or `statusName` to filter, not both. | | `statusName` | query | string | no | Optional: only return orders whose current workflow status has this exact name (case-sensitive). An alternative to `statusId` when you know the name but not the id — for example `statusName=Submitted`. Call GET /v1/company/order-statuses to see available names. | | `paymentStatus` | query | string | no | Optional: only return orders with this payment status. Unlike workflow status, payment status is a fixed set: UNPAID, PARTIALLY_PAID, or PAID. Leave unset to include all. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `receivedStatus` | query | string | no | Optional: only return orders with this received status. Unlike workflow status, received status is a fixed set: DELIVERED, PICKED_UP, NOT_RECEIVED, or INSTALLED. Leave unset to include all. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `fulfilmentMethod` | query | string | no | Optional: only return orders with this fulfilment method. A fixed set: DELIVER, PICK_UP, or INSTALL. Leave unset to include all. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `requiredAfter` | query | string | no | Only orders required at or after this time. Optional; filters on the order's required-by date, independent of the other filters. | | `requiredBefore` | query | string | no | Only orders required strictly before this time. Optional; combine with `requiredAfter` to bound a required-by window. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListOrdersResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListOrdersResponse` | Field | Type | Description | | --- | --- | --- | | `orders` | Order[] | The orders in this page. | | `orders.orderId` | string | The order's unique id. Read-only. | | `orders.isSubmitted` | boolean | Whether the order has been submitted (true) or is still a draft (false). | | `orders.customerId` | string | The id of the customer this order belongs to. Always present. Read-only. | | `orders.customerCompanyName` | string | The customer's company name. Read-only. | | `orders.statusId` | string | The id of the order's workflow status. Defaults to a "Submitted" status when the order is submitted without one set. | | `orders.statusName` | string | The display name of the order's workflow status. Read-only. | | `orders.subtotal` | Money | Order subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `orders.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.taxAmount` | Money | Total tax for the order, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `orders.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.total` | Money | Order grand total. Derived by the server; read-only. | | `orders.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.margin` | Money | Order margin, computed by the server from the line items. Read-only. | | `orders.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.totalCost` | Money | Total cost of the order, computed by the server from the line items. Read-only. | | `orders.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.lines` | SalesLine[] | The order's line items. | | `orders.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `orders.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `orders.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `orders.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `orders.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `orders.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `orders.lines.flashing.productName` | string | The display name of the flashing material. | | `orders.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `orders.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `orders.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `orders.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `orders.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `orders.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `orders.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `orders.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `orders.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `orders.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `orders.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `orders.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `orders.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `orders.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `orders.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `orders.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `orders.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `orders.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `orders.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `orders.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `orders.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `orders.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `orders.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `orders.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `orders.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `orders.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `orders.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `orders.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `orders.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `orders.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `orders.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `orders.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `orders.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `orders.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `orders.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `orders.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `orders.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `orders.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `orders.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `orders.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `orders.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `orders.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `orders.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `orders.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `orders.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `orders.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `orders.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `orders.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `orders.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `orders.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `orders.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `orders.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `orders.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `orders.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `orders.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `orders.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `orders.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `orders.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `orders.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `orders.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `orders.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `orders.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `orders.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `orders.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `orders.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `orders.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `orders.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `orders.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `orders.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `orders.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `orders.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `orders.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `orders.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `orders.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `orders.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `orders.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `orders.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `orders.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `orders.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `orders.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `orders.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `orders.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `orders.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `orders.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `orders.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `orders.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `orders.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `orders.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `orders.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `orders.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `orders.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `orders.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `orders.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `orders.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `orders.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `orders.adjustments` | Adjustment[] | Order-level fees, discounts, and markups. At most one fee per order is allowed. | | `orders.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `orders.adjustments.title` | string | A label describing the adjustment. | | `orders.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `orders.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `orders.fulfilmentMethod` | FulfilmentMethod | How the customer receives the order: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `orders.billingAddress` | Address | Billing address for the order. | | `orders.billingAddress.address1` | string | First address line (street number and name). | | `orders.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `orders.billingAddress.city` | string | City or suburb. | | `orders.billingAddress.state` | string | State, province, or region. | | `orders.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `orders.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `orders.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `orders.deliveryAddress.address1` | string | First address line (street number and name). | | `orders.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `orders.deliveryAddress.city` | string | City or suburb. | | `orders.deliveryAddress.state` | string | State, province, or region. | | `orders.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `orders.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `orders.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `orders.installAddress.address1` | string | First address line (street number and name). | | `orders.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `orders.installAddress.city` | string | City or suburb. | | `orders.installAddress.state` | string | State, province, or region. | | `orders.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `orders.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `orders.reference` | string | Free-text external reference, e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. | | `orders.orderNumber` | string | Factory's number for this order. Assigned by the server when the document was created (a quote keeps its number when it becomes an order), sequential within your company, and never changed. Shown as "Order #" in the Factory app (order list, order page, workflow board) and on the order confirmation, invoice, work order, and delivery docket, and used as the document reference in the accounting integrations. Read-only. | | `orders.receivedStatus` | ReceivedStatus | Whether the order has been received. Read-only. Whether and how the order has reached the customer. Read-only. - RECEIVED_STATUS_UNSPECIFIED: Default, unset value. - RECEIVED_STATUS_DELIVERED: The order was delivered. - RECEIVED_STATUS_PICKED_UP: The order was picked up by the customer. - RECEIVED_STATUS_NOT_RECEIVED: The order has not yet reached the customer. - RECEIVED_STATUS_INSTALLED: The order was installed. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `orders.paymentStatus` | PaymentStatus | The order's payment status. Read-only. Whether the order has been paid for. Read-only. - PAYMENT_STATUS_UNSPECIFIED: Default, unset value. - PAYMENT_STATUS_UNPAID: No payment has been received. - PAYMENT_STATUS_PARTIALLY_PAID: Part of the order total has been paid. - PAYMENT_STATUS_PAID: The order has been paid in full. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `orders.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `orders.createdAt` | string | When the order was created. Read-only. | | `orders.submittedAt` | string | When the order was submitted. Read-only. | | `orders.lastUpdatedAt` | string | When the order was last updated. Read-only. | | `orders.requiredAt` | string | The date the order is required or needed by. | | `orders.contact` | Contact | The order's point-of-contact person. | | `orders.contact.name` | string | The contact person's name. | | `orders.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `orders.contact.phone` | string | The contact's landline phone number. | | `orders.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `orders.notes` | string | Free-text notes about the order. | | `orders.pickupNotes` | string | Free-text notes shown when the order is picked up. | | `orders.deliveryFee` | Money | The delivery fee charged on the order. Read-only. | | `orders.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.createdByUserId` | string | The id of the user who created the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `orders.submittedByUserId` | string | The id of the user who submitted the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `orders.discountAmount` | Money | The order-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `orders.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.invoiceNumber` | string | The order's invoice number as displayed: the order's own number, or the linked accounting system's number when synced. Empty until the order is invoiced. Read-only. | | `orders.invoicedAt` | string | When the order was invoiced. Unset until the order is invoiced. Read-only. | | `orders.statusColour` | string | The colour of the order's workflow status, for display (defaults to "transparent"). The display companion to `statusName`. Read-only. | | `orders.tax` | TaxDetail | The tax descriptor for the order: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `orders.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `orders.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `orders.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `orders.labels` | Label[] | The labels attached to this order, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `orders.labels.labelId` | string | The label's id. | | `orders.labels.name` | string | Display name of the label. Unique within your company. | | `orders.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `orders.isArchived` | boolean | Whether the order is archived. Archived orders are hidden from every read by default: reach them with GetOrder's `includeArchived`, ListOrders' archived, or QueryOrders' `includeArchived`. Always false on default reads. Read-only. | | `orders.labourTotal` | Money | The total charged for labour on the order: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `orders.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/orders **Service:** Orders **Operation:** `OrderService_CreateOrder` The primary write endpoint: submit a complete order — header, line items, adjustments, and addresses — in one call, either saved as a draft or submitted immediately. The order belongs to the company identified by the bearer token. To make a retry safe, send an idempotency key in `requestId`; a repeat with the same key can never create a second order — it is rejected with HTTP 409 carrying the original order's id. A customer that can't be resolved or an unknown catalogue id is rejected with HTTP 400 (validation_failure). Tax and currency follow your account's settings: a single tax rate and a single currency. You may send a `tax` descriptor (rate, code, jurisdiction) and amounts in any currency, but in this version they are advisory — the server applies your account's configured rate to every taxable line and treats every amount as being in your account's currency: amounts are stored as the numbers you send and never converted, so an amount sent in another currency reads back as the same number in your account's currency. A line is exempt when you send `isTaxFree: true` on it, or when you omit `isTaxFree` and the customer has `disableTaxByDefault`; on a kit the components carry that default and the kit line's own flag is read first. See `isTaxFree` on each line kind for the full rule, including flashing lines, which are taxed regardless in this version. It can't yet apply a different rate or currency per line, order, or jurisdiction, such as VAT or a 15% rate. The created order carries the tax amount and currency actually applied (`taxAmount`), so compare them to your input to detect any override; if your source system priced the order differently, the recomputed total can differ from your source document. ## Request body `CreateOrderRequest` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `customerId` | string | no | The customer's unique id, as returned by the Customers API. Exactly one of `customerId` or `companyName` must be set; in this version only `customerId` is accepted. | | `companyName` | string | no | The customer's company name. Not yet supported: a request that sets `companyName` instead of `customerId` is rejected with HTTP 501 (not_implemented). Resolve the name with ListCustomers and send `customerId`. | | `lines` | SalesLine[] | no | The order's line items. Each line is one of the supported line types (on-the-fly, catalogue, labour, notes, flashing, or product kit). | | `lines.onTheFly` | OnTheFlyLine | no | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `lines.onTheFly.productName` | string | yes | Name of the item. Required. | | `lines.onTheFly.productDescription` | string | no | Optional longer description of the item. | | `lines.onTheFly.colour` | string | no | Optional colour for the item. | | `lines.onTheFly.pricing` | LinePricing | no | Pricing for this line. Unit price is required; the line total is required until server-side derivation is enabled for your account (see `totalPrice` on LinePricing), except on custom-formula lines, where it is always required. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.onTheFly.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.onTheFly.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.onTheFly.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.onTheFly.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.onTheFly.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.onTheFly.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.onTheFly.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.onTheFly.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.onTheFly.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.onTheFly.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.onTheFly.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.onTheFly.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.onTheFly.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.onTheFly.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.onTheFly.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.onTheFly.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.onTheFly.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.onTheFly.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.onTheFly.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.onTheFly.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.onTheFly.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. | | `lines.catalogue` | CatalogueLine | no | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `lines.catalogue.productId` | string | yes | The id of the catalogue product for this line. Required, and must match an existing catalogue product; an unknown id rejects the whole request. | | `lines.catalogue.productRowId` | string | no | Optional id of a specific row within the catalogue product. It must be one of the product's rows. The row supplies the line's attributes, cost and unit price when you leave those out. | | `lines.catalogue.rowPriceId` | string | no | Optional id selecting the row's price at one of your price levels — the `rowPriceId` returned by the catalogue (not the price level's own id). It must be a price of `productRowId`; sent without `productRowId`, it must be a price of one of the product's rows, and that row becomes the line's. When you send no `unitPrice`, the line is priced at this level. | | `lines.catalogue.productName` | string | no | Display name for the line. Defaults to the product's name. | | `lines.catalogue.productDescription` | string | no | Longer description for the line. | | `lines.catalogue.categoryName` | string | no | Category name for the line. Defaults to the product's category. | | `lines.catalogue.priceName` | string | no | Price name shown for the line. | | `lines.catalogue.params` | object[] | no | Product parameters as a JSON array of {name, value} entries (for example [{"name": "Colour", "value": "Monument"}]). The colour is taken from the "Colour" parameter and validated against the product's available colours. | | `lines.catalogue.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. Defaults to the row's attributes when the line names a row. | | `lines.catalogue.pricing` | LinePricing | no | Pricing for this line. Parts you leave out come from the catalogue: `pricingStrategy` from the product; `cost` from the row; `unitPrice` from the row's price at `rowPriceId`, or the row's marked-up price (with the row's `markup`) when no price level is chosen. A line that names no row takes only the strategy, and `unitPrice` is yours to send. An omitted `isTaxFree` follows the customer and the product's tax-free setting (see `isTaxFree`). `quantity`, measurements and `totalPrice` follow the rules of every priced line. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.catalogue.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.catalogue.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.catalogue.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.catalogue.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.catalogue.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.catalogue.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.catalogue.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.catalogue.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.catalogue.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.catalogue.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.catalogue.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.catalogue.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.catalogue.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.catalogue.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.catalogue.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.catalogue.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.catalogue.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.catalogue.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.catalogue.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.catalogue.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.labour` | LabourLine | no | A line charging for labour. Set exactly one of the six line-type fields. | | `lines.labour.labourUserId` | string | yes | The id of the worker who performed the labour. Required, and must be an active user in the company that owns the order or quote. | | `lines.labour.productName` | string | no | A short name for the labour. | | `lines.labour.productDescription` | string | no | A longer description of the labour. | | `lines.labour.pricing` | LinePricing | no | Quantity and pricing for this line: the hours in `quantity`, the charge rate in `unitPrice` and the cost rate in `cost`. There are no rate overrides elsewhere on the line, and the worker's own rates are not applied: an omitted `cost` is stored as zero, not taken from the user's `hourlyRateCost`. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.labour.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.labour.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.labour.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.labour.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.labour.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.labour.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.labour.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.labour.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.labour.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.labour.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.labour.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.labour.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.labour.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.labour.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.labour.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.labour.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.labour.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.labour.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.labour.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.labour.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.notes` | NotesLine | no | A note line, with no pricing. Set exactly one of the six line-type fields. | | `lines.notes.notesType` | NotesType | yes | The kind of note. Required. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `lines.notes.notes` | string | no | The note text. | | `lines.notes.productName` | string | no | A short name for the note line. | | `lines.notes.productDescription` | string | no | A longer description for the note line. | | `lines.flashing` | FlashingLine | no | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `lines.flashing.templateId` | string | no | The id of the flashing material or template. Required when the order or quote is submitted. | | `lines.flashing.productName` | string | no | The display name of the flashing material. | | `lines.flashing.priceLevel` | string | no | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `lines.flashing.colour` | string | no | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `lines.flashing.thickness` | string | no | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `lines.flashing.bends` | string | no | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `lines.flashing.totalGirth` | string | no | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `lines.flashing.totalLength` | string | no | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `lines.flashing.unitPrice` | Money | no | The price per metre of the flashing. Required when the order or quote is submitted. | | `lines.flashing.totalPrice` | Money | no | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `lines.flashing.customPrice` | Money | no | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `lines.flashing.customPricePerLength` | Money | no | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `lines.flashing.subitems` | object[] | no | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `lines.flashing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `lines.flashing.drawing` | FlashingDrawing | yes | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `lines.flashing.drawing.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `lines.flashing.drawing.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `lines.flashing.drawing.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `lines.flashing.drawing.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `lines.flashing.drawing.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `lines.flashing.drawing.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `lines.flashing.drawing.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `lines.flashing.drawing.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `lines.flashing.drawing.points.*.id` | string | no | The unique id of this point within the drawing. | | `lines.flashing.drawing.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `lines.flashing.drawing.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `lines.flashing.drawing.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `lines.flashing.drawing.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `lines.flashing.drawing.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `lines.flashing.drawing.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `lines.flashing.drawing.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `lines.flashing.drawing.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `lines.flashing.drawing.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `lines.flashing.drawing.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.drawing.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.drawing.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.drawing.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.drawing.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.drawing.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `lines.flashing.drawing.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `lines.flashing.drawing.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `lines.flashing.drawing.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `lines.flashing.drawing.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.drawing.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.drawing.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.drawing.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.drawing.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.drawing.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `lines.flashing.drawing.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `lines.flashing.drawing.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `lines.flashing.drawing.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `lines.flashing.drawing.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.drawing.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.drawing.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.drawing.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.drawing.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.drawing.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `lines.flashing.drawing.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.drawing.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.drawing.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `lines.flashing.drawing.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `lines.flashing.drawing.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `lines.flashing.drawing.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `lines.flashing.drawing.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `lines.flashing.drawing.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `lines.flashing.drawing.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `lines.flashing.drawing.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `lines.flashing.drawing.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `lines.flashing.drawing.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.drawing.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.drawing.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `lines.flashing.drawing.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `lines.flashing.drawing.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.drawing.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.drawing.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `lines.flashing.otherSides` | FlashingDrawing[] | no | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `lines.flashing.otherSides.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `lines.flashing.otherSides.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `lines.flashing.otherSides.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `lines.flashing.otherSides.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `lines.flashing.otherSides.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `lines.flashing.otherSides.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `lines.flashing.otherSides.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `lines.flashing.otherSides.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `lines.flashing.otherSides.points.*.id` | string | no | The unique id of this point within the drawing. | | `lines.flashing.otherSides.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `lines.flashing.otherSides.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `lines.flashing.otherSides.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `lines.flashing.otherSides.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `lines.flashing.otherSides.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `lines.flashing.otherSides.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `lines.flashing.otherSides.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `lines.flashing.otherSides.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `lines.flashing.otherSides.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `lines.flashing.otherSides.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.otherSides.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.otherSides.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.otherSides.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `lines.flashing.otherSides.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `lines.flashing.otherSides.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `lines.flashing.otherSides.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `lines.flashing.otherSides.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.otherSides.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.otherSides.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.otherSides.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `lines.flashing.otherSides.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `lines.flashing.otherSides.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `lines.flashing.otherSides.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `lines.flashing.otherSides.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.otherSides.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.otherSides.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.otherSides.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `lines.flashing.otherSides.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `lines.flashing.otherSides.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `lines.flashing.otherSides.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `lines.flashing.otherSides.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `lines.flashing.otherSides.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `lines.flashing.otherSides.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `lines.flashing.otherSides.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `lines.flashing.otherSides.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `lines.flashing.otherSides.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `lines.flashing.otherSides.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `lines.flashing.otherSides.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `lines.flashing.otherSides.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `lines.flashing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.productKit` | ProductKitLine | no | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `lines.productKit.kitId` | string | no | The kit's id (`kitId` on Kit, from the catalogue read surface). Optional: omit it to define an on-the-fly kit not linked to any catalogue kit — the kit is described entirely by this line's name, pricing, components, and sub-kits. | | `lines.productKit.kitRowId` | string | no | The selected kit row/variant id. Optional. | | `lines.productKit.productName` | string | no | Display name of the kit. | | `lines.productKit.customPricing` | boolean | no | Whether the kit is custom-priced (true) rather than computed from its components (false). | | `lines.productKit.description` | string | no | Free-text description of the kit. | | `lines.productKit.displayKitItems` | object[] | no | Which documents and views show the kit's components, as a JSON array of display-target keys. The keys are "displayOnAllViews", "displayOnWorkOrderPdf", "displayOnDeliveryDocketPdf", "displayOnInvoicePdf", "displayOnQuotePdf", "displayOnProformaInvoicePdf", "displayOnCustomerOnlineView" and "displayOnAccountingPackage"; a key that is not one of these matches no document. Optional: when omitted, the kit takes your company's "Display individual Product Kit items on" setting, as a kit built in the Factory app does. An empty array shows the components on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `lines.productKit.displaySubKits` | object[] | no | Which documents and views show the kit's sub-kits, as a JSON array of the same display-target keys as `displayKitItems`. Optional: when omitted, the kit takes your company's "Display Product Kit sub-kits on" setting, as a kit built in the Factory app does. An empty array shows the sub-kits on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `lines.productKit.pricing` | LinePricing | no | Overall pricing for the kit line. Its `isTaxFree` is read before the components': a tax-free kit line exempts the whole kit whatever the components say, a taxable one lets each component's own `isTaxFree` decide, and a custom-priced kit is taxed on the kit line alone. Omit it and it depends on the kit: on a standard kit it is false, with the components following the customer, so a component you mark taxable is charged even for an exempt customer; on a custom-priced kit it follows the customer, so an exempt customer's custom kit is exempt in full. A value you send, true or false, is kept as sent. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.productKit.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.productKit.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.productKit.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.productKit.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.productKit.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.productKit.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.productKit.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.productKit.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.productKit.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.productKit.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.productKit.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.productKit.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.productKit.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.attributes` | object[] | no | Additional named attributes for the kit line, as a JSON array of {name, value} entries; for measurement-priced kits they describe the measurement entries. | | `lines.productKit.components` | KitComponent[] | no | The kit's direct components. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `lines.productKit.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `lines.productKit.components.productId` | string | no | The catalogue product id for this component. Optional. | | `lines.productKit.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `lines.productKit.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `lines.productKit.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `lines.productKit.components.productDescription` | string | no | Free-text description of the component. | | `lines.productKit.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `lines.productKit.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `lines.productKit.components.unitPrice` | Money | no | Price per unit. | | `lines.productKit.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `lines.productKit.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `lines.productKit.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `lines.productKit.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `lines.productKit.components.cost` | Money | no | Cost of the component. | | `lines.productKit.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `lines.productKit.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `lines.productKit.components.margin` | Money | no | Margin earned on the component. | | `lines.productKit.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.productKit.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `lines.productKit.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `lines.productKit.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `lines.productKit.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `lines.productKit.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `lines.productKit.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `lines.productKit.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `lines.productKit.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `lines.productKit.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `lines.productKit.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.subKits` | SubKit[] | no | The kit's sub-assemblies, each with its own components. A sub-assembly within a kit. A sub-kit groups a set of components under its own title and quantity, letting a kit be organized into named sub-assemblies. | | `lines.productKit.subKits.title` | string | no | Display title of the sub-assembly. | | `lines.productKit.subKits.quantity` | string | no | Quantity of this sub-assembly, as a decimal string. | | `lines.productKit.subKits.actualQuantity` | string | no | Actual measured quantity, as a decimal string. | | `lines.productKit.subKits.unitPrice` | Money | no | Price per unit of the sub-assembly. | | `lines.productKit.subKits.totalPrice` | Money | no | Total price of the sub-assembly. | | `lines.productKit.subKits.components` | KitComponent[] | no | The components that make up this sub-assembly. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `lines.productKit.subKits.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `lines.productKit.subKits.components.productId` | string | no | The catalogue product id for this component. Optional. | | `lines.productKit.subKits.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `lines.productKit.subKits.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `lines.productKit.subKits.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `lines.productKit.subKits.components.productDescription` | string | no | Free-text description of the component. | | `lines.productKit.subKits.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `lines.productKit.subKits.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `lines.productKit.subKits.components.unitPrice` | Money | no | Price per unit. | | `lines.productKit.subKits.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `lines.productKit.subKits.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `lines.productKit.subKits.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `lines.productKit.subKits.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `lines.productKit.subKits.components.cost` | Money | no | Cost of the component. | | `lines.productKit.subKits.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `lines.productKit.subKits.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `lines.productKit.subKits.components.margin` | Money | no | Margin earned on the component. | | `lines.productKit.subKits.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.productKit.subKits.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `lines.productKit.subKits.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `lines.productKit.subKits.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `lines.productKit.subKits.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `lines.productKit.subKits.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `lines.productKit.subKits.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `lines.productKit.subKits.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `lines.productKit.subKits.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `lines.productKit.subKits.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `lines.productKit.subKits.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.subKits.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.subKits.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.subKits.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.subKits.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.subKits.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.subKits.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.subKits.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.id` | string | no | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `adjustments` | Adjustment[] | no | Order-level fees, discounts, and markups applied across the whole order. | | `adjustments.type` | AdjustmentType | yes | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `adjustments.title` | string | no | A label describing the adjustment. | | `adjustments.amount` | Money | no | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `adjustments.amount.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `adjustments.amount.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `adjustments.percent` | string | no | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `fulfilmentMethod` | FulfilmentMethod | no | How the customer receives the order: pickup, delivery, or installation. Determines which address below applies. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `billingAddress` | Address | no | Billing address for the order. | | `billingAddress.address1` | string | no | First address line (street number and name). | | `billingAddress.address2` | string | no | Second address line (unit, suite, or similar). | | `billingAddress.city` | string | no | City or suburb. | | `billingAddress.state` | string | no | State, province, or region. | | `billingAddress.postalCode` | string | no | Postal code (ZIP code, postcode). | | `billingAddress.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `deliveryAddress` | Address | no | Delivery address. Used only when `fulfilmentMethod` is DELIVERY. | | `deliveryAddress.address1` | string | no | First address line (street number and name). | | `deliveryAddress.address2` | string | no | Second address line (unit, suite, or similar). | | `deliveryAddress.city` | string | no | City or suburb. | | `deliveryAddress.state` | string | no | State, province, or region. | | `deliveryAddress.postalCode` | string | no | Postal code (ZIP code, postcode). | | `deliveryAddress.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `installAddress` | Address | no | Installation address. Used only when `fulfilmentMethod` is INSTALL. | | `installAddress.address1` | string | no | First address line (street number and name). | | `installAddress.address2` | string | no | Second address line (unit, suite, or similar). | | `installAddress.city` | string | no | City or suburb. | | `installAddress.state` | string | no | State, province, or region. | | `installAddress.postalCode` | string | no | Postal code (ZIP code, postcode). | | `installAddress.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `isSubmitted` | boolean | no | Submit the order immediately (true) or save it as a draft (false). Submitting assigns a status and finalizes the order. | | `statusId` | string | no | Optional explicit workflow status to place the order in, by id. If omitted, a default status is applied when the order is submitted. Discover your account's statuses with GET /v1/company/order-statuses. | | `reference` | string | no | Free-text external reference for this order — e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. Stored for lookup and audit; distinct from `requestId` (the retry key) and from the read-only `orderNumber` (Factory's own number for the order). | | `customFields` | object | no | Values for your account's custom-defined sales-document fields, keyed by each field's configured key. Unknown keys, keys of other modules, and values that don't match the field's type are rejected. Send number and currency values as numbers or decimal strings, checkboxes as booleans, multi-selects as arrays of strings, and everything else as strings. Select and multi-select values must be option keys from the field's definition, never display labels; discover your account's field keys and options with GET /v1/company/custom-fields. Formula fields are computed and cannot be written. | | `syncAccounting` | boolean | no | Whether to sync the order to your accounting system (Xero, MYOB, or QBO) when it is submitted. Defaults to false; leave it false for bulk imports to avoid syncing every order. | | `requiredAt` | string | no | The date the order is required or needed by. | | `contact` | Contact | no | The point-of-contact person for this order, distinct from the customer. | | `contact.name` | string | no | The contact person's name. | | `contact.email` | string | no | The contact's email address. Optional; when provided, it must be a valid email address. | | `contact.phone` | string | no | The contact's landline phone number. | | `contact.mobile` | string | no | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `notes` | string | no | Free-text notes about the order. | | `pickupNotes` | string | no | Free-text notes shown when the order is picked up. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `tax` | TaxDetail | no | The tax to apply, as a rate/code/jurisdiction descriptor. Optional and advisory in this version: the server applies your account's configured tax rate regardless of what you send here, and the created order echoes back the values actually applied (`tax` and `taxAmount` on the Order) — compare them to detect an override. Reserves the shape for future per-order tax support. | | `tax.rate` | string | no | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `tax.code` | string | no | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `tax.jurisdiction` | string | no | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `labelIds` | string[] | no | Labels to attach to the order as part of creating it, by id. Every id must exist in your company's label set (discover them with GET /v1/company/labels): an unknown id fails the whole call with HTTP 404 before the order is created. Labelling happens with the create but not atomically inside it — in the rare case the order is created and labelling then fails, the error names the created order id and the order exists WITHOUT labels; attach them with SetOrderLabels. The created order echoes the attached labels. Duplicate ids are rejected. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `CreateOrderResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the order it created), its outcome is still unknown, or the key was reused with a materially different body. Verify the existing order with GetOrder or ListOrders, then send a new `requestId` to create a new order. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `CreateOrderResponse` | Field | Type | Description | | --- | --- | --- | | `order` | Order | The created order, including server-set fields and assigned identifiers. | | `order.orderId` | string | The order's unique id. Read-only. | | `order.isSubmitted` | boolean | Whether the order has been submitted (true) or is still a draft (false). | | `order.customerId` | string | The id of the customer this order belongs to. Always present. Read-only. | | `order.customerCompanyName` | string | The customer's company name. Read-only. | | `order.statusId` | string | The id of the order's workflow status. Defaults to a "Submitted" status when the order is submitted without one set. | | `order.statusName` | string | The display name of the order's workflow status. Read-only. | | `order.subtotal` | Money | Order subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `order.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.taxAmount` | Money | Total tax for the order, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `order.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.total` | Money | Order grand total. Derived by the server; read-only. | | `order.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.margin` | Money | Order margin, computed by the server from the line items. Read-only. | | `order.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.totalCost` | Money | Total cost of the order, computed by the server from the line items. Read-only. | | `order.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.lines` | SalesLine[] | The order's line items. | | `order.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `order.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `order.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `order.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `order.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `order.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `order.lines.flashing.productName` | string | The display name of the flashing material. | | `order.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `order.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `order.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `order.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `order.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `order.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `order.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `order.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `order.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `order.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `order.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `order.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `order.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `order.adjustments` | Adjustment[] | Order-level fees, discounts, and markups. At most one fee per order is allowed. | | `order.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `order.adjustments.title` | string | A label describing the adjustment. | | `order.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `order.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `order.fulfilmentMethod` | FulfilmentMethod | How the customer receives the order: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `order.billingAddress` | Address | Billing address for the order. | | `order.billingAddress.address1` | string | First address line (street number and name). | | `order.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.billingAddress.city` | string | City or suburb. | | `order.billingAddress.state` | string | State, province, or region. | | `order.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `order.deliveryAddress.address1` | string | First address line (street number and name). | | `order.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.deliveryAddress.city` | string | City or suburb. | | `order.deliveryAddress.state` | string | State, province, or region. | | `order.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `order.installAddress.address1` | string | First address line (street number and name). | | `order.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.installAddress.city` | string | City or suburb. | | `order.installAddress.state` | string | State, province, or region. | | `order.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.reference` | string | Free-text external reference, e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. | | `order.orderNumber` | string | Factory's number for this order. Assigned by the server when the document was created (a quote keeps its number when it becomes an order), sequential within your company, and never changed. Shown as "Order #" in the Factory app (order list, order page, workflow board) and on the order confirmation, invoice, work order, and delivery docket, and used as the document reference in the accounting integrations. Read-only. | | `order.receivedStatus` | ReceivedStatus | Whether the order has been received. Read-only. Whether and how the order has reached the customer. Read-only. - RECEIVED_STATUS_UNSPECIFIED: Default, unset value. - RECEIVED_STATUS_DELIVERED: The order was delivered. - RECEIVED_STATUS_PICKED_UP: The order was picked up by the customer. - RECEIVED_STATUS_NOT_RECEIVED: The order has not yet reached the customer. - RECEIVED_STATUS_INSTALLED: The order was installed. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `order.paymentStatus` | PaymentStatus | The order's payment status. Read-only. Whether the order has been paid for. Read-only. - PAYMENT_STATUS_UNSPECIFIED: Default, unset value. - PAYMENT_STATUS_UNPAID: No payment has been received. - PAYMENT_STATUS_PARTIALLY_PAID: Part of the order total has been paid. - PAYMENT_STATUS_PAID: The order has been paid in full. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `order.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `order.createdAt` | string | When the order was created. Read-only. | | `order.submittedAt` | string | When the order was submitted. Read-only. | | `order.lastUpdatedAt` | string | When the order was last updated. Read-only. | | `order.requiredAt` | string | The date the order is required or needed by. | | `order.contact` | Contact | The order's point-of-contact person. | | `order.contact.name` | string | The contact person's name. | | `order.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `order.contact.phone` | string | The contact's landline phone number. | | `order.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `order.notes` | string | Free-text notes about the order. | | `order.pickupNotes` | string | Free-text notes shown when the order is picked up. | | `order.deliveryFee` | Money | The delivery fee charged on the order. Read-only. | | `order.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.createdByUserId` | string | The id of the user who created the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.submittedByUserId` | string | The id of the user who submitted the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.discountAmount` | Money | The order-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `order.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.invoiceNumber` | string | The order's invoice number as displayed: the order's own number, or the linked accounting system's number when synced. Empty until the order is invoiced. Read-only. | | `order.invoicedAt` | string | When the order was invoiced. Unset until the order is invoiced. Read-only. | | `order.statusColour` | string | The colour of the order's workflow status, for display (defaults to "transparent"). The display companion to `statusName`. Read-only. | | `order.tax` | TaxDetail | The tax descriptor for the order: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `order.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `order.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `order.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `order.labels` | Label[] | The labels attached to this order, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `order.labels.labelId` | string | The label's id. | | `order.labels.name` | string | Display name of the label. Unique within your company. | | `order.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `order.isArchived` | boolean | Whether the order is archived. Archived orders are hidden from every read by default: reach them with GetOrder's `includeArchived`, ListOrders' archived, or QueryOrders' `includeArchived`. Always false on default reads. Read-only. | | `order.labourTotal` | Money | The total charged for labour on the order: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `order.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `drawings` | DrawingIdMapping[] | The server identities of the flashing drawings created by this call, one entry per flashing line in the order you sent them, each pairing your `tempId` with the assigned `drawingId` — use them to address UploadDrawingSvg. Only flashing lines carry drawings; empty when the order had no flashing lines. | | `drawings.tempId` | string | The `tempId` you supplied on the drawing, echoed verbatim; empty when you did not supply one (position in the list still identifies the drawing). | | `drawings.drawingId` | string | The server-assigned id of the drawing. | | `drawings.otherSides` | DrawingIdMapping[] | The identities of the drawing's other sides, in the order you sent them. | | `drawings.otherSides.tempId` | string | The `tempId` you supplied on the drawing, echoed verbatim; empty when you did not supply one (position in the list still identifies the drawing). | | `drawings.otherSides.drawingId` | string | The server-assigned id of the drawing. | | `drawings.otherSides.otherSides` | DrawingIdMapping[] | The identities of the drawing's other sides, in the order you sent them. | --- # GET /v1/orders/{orderId} **Service:** Orders **Operation:** `OrderService_GetOrder` Returns the full order — header, lines, adjustments, totals, and server-derived fields — for the given id, scoped to the authenticated company. Resolves confirmed orders only: an id that belongs to a quote returns HTTP 404 (not_found) — read quotes with GetQuote. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order to retrieve. Required. | | `includeArchived` | query | boolean | no | Optional: also resolve the order if it is archived. By default an archived order is not found (404); set this to true to read it anyway — the returned order carries `isArchived`: true. Archived orders stay read-only: this flag never makes them writable. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetOrderResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, the order is archived (pass `includeArchived=true` to read it anyway), or the document is still a quote — read it with GetQuote, swapping the id's order_ prefix for quote_ (the 26-character suffix stays the same). | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetOrderResponse` | Field | Type | Description | | --- | --- | --- | | `order` | Order | The requested order. | | `order.orderId` | string | The order's unique id. Read-only. | | `order.isSubmitted` | boolean | Whether the order has been submitted (true) or is still a draft (false). | | `order.customerId` | string | The id of the customer this order belongs to. Always present. Read-only. | | `order.customerCompanyName` | string | The customer's company name. Read-only. | | `order.statusId` | string | The id of the order's workflow status. Defaults to a "Submitted" status when the order is submitted without one set. | | `order.statusName` | string | The display name of the order's workflow status. Read-only. | | `order.subtotal` | Money | Order subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `order.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.taxAmount` | Money | Total tax for the order, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `order.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.total` | Money | Order grand total. Derived by the server; read-only. | | `order.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.margin` | Money | Order margin, computed by the server from the line items. Read-only. | | `order.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.totalCost` | Money | Total cost of the order, computed by the server from the line items. Read-only. | | `order.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.lines` | SalesLine[] | The order's line items. | | `order.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `order.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `order.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `order.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `order.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `order.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `order.lines.flashing.productName` | string | The display name of the flashing material. | | `order.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `order.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `order.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `order.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `order.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `order.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `order.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `order.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `order.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `order.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `order.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `order.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `order.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `order.adjustments` | Adjustment[] | Order-level fees, discounts, and markups. At most one fee per order is allowed. | | `order.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `order.adjustments.title` | string | A label describing the adjustment. | | `order.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `order.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `order.fulfilmentMethod` | FulfilmentMethod | How the customer receives the order: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `order.billingAddress` | Address | Billing address for the order. | | `order.billingAddress.address1` | string | First address line (street number and name). | | `order.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.billingAddress.city` | string | City or suburb. | | `order.billingAddress.state` | string | State, province, or region. | | `order.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `order.deliveryAddress.address1` | string | First address line (street number and name). | | `order.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.deliveryAddress.city` | string | City or suburb. | | `order.deliveryAddress.state` | string | State, province, or region. | | `order.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `order.installAddress.address1` | string | First address line (street number and name). | | `order.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.installAddress.city` | string | City or suburb. | | `order.installAddress.state` | string | State, province, or region. | | `order.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.reference` | string | Free-text external reference, e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. | | `order.orderNumber` | string | Factory's number for this order. Assigned by the server when the document was created (a quote keeps its number when it becomes an order), sequential within your company, and never changed. Shown as "Order #" in the Factory app (order list, order page, workflow board) and on the order confirmation, invoice, work order, and delivery docket, and used as the document reference in the accounting integrations. Read-only. | | `order.receivedStatus` | ReceivedStatus | Whether the order has been received. Read-only. Whether and how the order has reached the customer. Read-only. - RECEIVED_STATUS_UNSPECIFIED: Default, unset value. - RECEIVED_STATUS_DELIVERED: The order was delivered. - RECEIVED_STATUS_PICKED_UP: The order was picked up by the customer. - RECEIVED_STATUS_NOT_RECEIVED: The order has not yet reached the customer. - RECEIVED_STATUS_INSTALLED: The order was installed. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `order.paymentStatus` | PaymentStatus | The order's payment status. Read-only. Whether the order has been paid for. Read-only. - PAYMENT_STATUS_UNSPECIFIED: Default, unset value. - PAYMENT_STATUS_UNPAID: No payment has been received. - PAYMENT_STATUS_PARTIALLY_PAID: Part of the order total has been paid. - PAYMENT_STATUS_PAID: The order has been paid in full. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `order.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `order.createdAt` | string | When the order was created. Read-only. | | `order.submittedAt` | string | When the order was submitted. Read-only. | | `order.lastUpdatedAt` | string | When the order was last updated. Read-only. | | `order.requiredAt` | string | The date the order is required or needed by. | | `order.contact` | Contact | The order's point-of-contact person. | | `order.contact.name` | string | The contact person's name. | | `order.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `order.contact.phone` | string | The contact's landline phone number. | | `order.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `order.notes` | string | Free-text notes about the order. | | `order.pickupNotes` | string | Free-text notes shown when the order is picked up. | | `order.deliveryFee` | Money | The delivery fee charged on the order. Read-only. | | `order.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.createdByUserId` | string | The id of the user who created the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.submittedByUserId` | string | The id of the user who submitted the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.discountAmount` | Money | The order-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `order.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.invoiceNumber` | string | The order's invoice number as displayed: the order's own number, or the linked accounting system's number when synced. Empty until the order is invoiced. Read-only. | | `order.invoicedAt` | string | When the order was invoiced. Unset until the order is invoiced. Read-only. | | `order.statusColour` | string | The colour of the order's workflow status, for display (defaults to "transparent"). The display companion to `statusName`. Read-only. | | `order.tax` | TaxDetail | The tax descriptor for the order: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `order.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `order.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `order.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `order.labels` | Label[] | The labels attached to this order, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `order.labels.labelId` | string | The label's id. | | `order.labels.name` | string | Display name of the label. Unique within your company. | | `order.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `order.isArchived` | boolean | Whether the order is archived. Archived orders are hidden from every read by default: reach them with GetOrder's `includeArchived`, ListOrders' archived, or QueryOrders' `includeArchived`. Always false on default reads. Read-only. | | `order.labourTotal` | Money | The total charged for labour on the order: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `order.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # GET /v1/orders/{orderId}/attachments **Service:** Orders **Operation:** `OrderService_ListAttachments` Returns every file on the order's conversation as a flat list, oldest first: files your team attached and documents the Factory app generated (`isGenerated` distinguishes them). Each attachment carries a presigned download URL. Signature captures collected in the app are not returned. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order whose attachments to list. Required. | | `pageSize` | query | integer | no | Maximum number of attachments to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `salesordersListAttachmentsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, or the document is still a quote — read its conversation with the QuoteService endpoint, swapping the id's order_ prefix for quote_ (the 26-character suffix stays the same). | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `salesordersListAttachmentsResponse` | Field | Type | Description | | --- | --- | --- | | `attachments` | Attachment[] | The attachments in this page, oldest first. | | `attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `attachments.filename` | string | The file's name, as uploaded. | | `attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/orders/{orderId}/attachments **Service:** Orders **Operation:** `OrderService_UploadAttachment` Uploads one file, up to 20 MB, into the order's conversation, where it appears immediately in the Factory app's Collaborate tab. Send the file content (base64-encoded over REST) with its filename and, optionally, a plain-text caption — with a caption the upload reads as a message carrying a file; without one it is a bare attachment, which is how most files are posted. The created conversation message is returned, including the attachment's id and a presigned download URL. HEIC and HEIF images are converted to JPEG on upload. To make a retry safe, send an idempotency key in `requestId`; a repeat with the same key can never attach the file twice — it is rejected with HTTP 409 carrying the original message's id. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order to attach to. Required. | ## Request body `OrderServiceUploadAttachmentBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `file` | string | yes | The file content, up to 20 MB. Required. | | `filename` | string | yes | The file's name, including its extension. Required. | | `contentType` | string | no | The file's media type (for example `image/png`). Optional; derived from the filename when omitted. | | `caption` | string | no | Optional plain-text caption. The upload always creates one conversation message carrying the file; the caption becomes that message's text, stored in the same HTML-escaped, `

`-wrapped form as a posted message. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `salesordersUploadAttachmentResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation — the body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). This includes a file over the 20 MB limit and a HEIC image that could not be converted. | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, or the document is still a quote — attach via the QuoteService endpoint, swapping the id's order_ prefix for quote_ (the 26-character suffix stays the same). | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the message it created), its outcome is still unknown, or the key was reused with a materially different body. Verify the conversation with ListMessages or ListAttachments, then send a new `requestId` to attach a new file. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `salesordersUploadAttachmentResponse` | Field | Type | Description | | --- | --- | --- | | `message` | Message | The created message, carrying the uploaded file as its single attachment (with its id and a presigned download URL). | | `message.id` | string | The id of this message. | | `message.authorName` | string | The display name of the person who wrote the message. Empty when the author's account no longer exists. | | `message.authorEmail` | string | The email address of the person who wrote the message. Empty when the author's account no longer exists. | | `message.text` | string | The message text, as an HTML fragment. Messages written in the Factory app carry the app's markup (including @-mentions); plain text posted through this API is HTML-escaped and `

`-wrapped on write, and reads back in that stored form. Empty for file-only messages — most attachments are posted without any text. | | `message.createdAt` | string | When the message was posted. | | `message.attachments` | Attachment[] | The files attached to this message, if any. | | `message.attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `message.attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `message.attachments.filename` | string | The file's name, as uploaded. | | `message.attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `message.attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `message.attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `message.attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `message.attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `message.attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | --- # GET /v1/orders/{orderId}/attachments/{attachmentId} **Service:** Orders **Operation:** `OrderService_GetAttachment` Returns the attachment's details and a fresh presigned download URL. Use this when a stored URL from an earlier read has expired. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order the attachment belongs to. Required. | | `attachmentId` | path | string | yes | The id of the attachment to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `salesordersGetAttachmentResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, the attachment is not part of this order's conversation, or it has been removed. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `salesordersGetAttachmentResponse` | Field | Type | Description | | --- | --- | --- | | `attachment` | Attachment | The requested attachment, with a fresh presigned download URL. | | `attachment.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `attachment.messageId` | string | The id of the conversation message this attachment belongs to. | | `attachment.filename` | string | The file's name, as uploaded. | | `attachment.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `attachment.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `attachment.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `attachment.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `attachment.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `attachment.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | --- # DELETE /v1/orders/{orderId}/attachments/{attachmentId} **Service:** Orders **Operation:** `OrderService_DeleteAttachment` Removes the file from the order's conversation and from attachment lists. The removal is reversible by a Factory app administrator. Only the person who posted the file or an administrator can remove it; for an API key, the acting user is the person the key belongs to, so a key can always remove files it uploaded. Documents generated by the Factory app (`isGenerated`) cannot be removed. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order the attachment belongs to. Required. | | `attachmentId` | path | string | yes | The id of the attachment to remove. Required. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `salesordersDeleteAttachmentResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation, or the attachment is a document generated by the Factory app, which cannot be removed (failed_precondition). The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The acting user did not post this file and is not an administrator. | | 404 | `Error` | No order with the given id exists for the authenticated company, the attachment is not part of this order's conversation, or it has already been removed. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | --- # POST /v1/orders/{orderId}/drawings:uploadSvg **Service:** Orders **Operation:** `OrderService_UploadDrawingSvg` Drawings exist only on flashing lines: a drawing is the folded profile of a sheet-metal flashing, not a general image or file attachment for the order. An order with no flashing lines has no drawings, and this is the only surface that accepts an upload. Call this after creating an order: map each drawing's `tempId` to the `drawingId` returned on the created order, then upload the rendered SVG for each `drawingId`. Stored SVGs are returned as a presigned URL (`svgUrl` on the FlashingDrawing) on later reads. Each drawing's outcome is reported individually; a drawing is skipped if it isn't part of this order, isn't an image/svg+xml, or has been deleted. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order whose flashing drawings are being uploaded. Required. | ## Request body `OrderServiceUploadDrawingSvgBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `svgs` | DrawingSvg[] | yes | The flashing drawings and their rendered SVGs. At least one is required. | | `svgs.drawingId` | string | yes | The id of the flashing drawing this SVG belongs to. Required. | | `svgs.svg` | string | yes | The rendered SVG image content, sent base64-encoded in JSON. The decoded image must be at most 2,000,000 bytes (the base64 text is about a third larger). Required. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `salesordersUploadDrawingSvgResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `salesordersUploadDrawingSvgResponse` | Field | Type | Description | | --- | --- | --- | | `results` | DrawingSvgResult[] | One result per submitted flashing drawing, in request order. | | `results.drawingId` | string | The drawing this result refers to. | | `results.uploaded` | boolean | Whether the SVG was stored successfully. | | `results.svgUrl` | string | On success, a presigned URL to the stored SVG. | | `results.error` | string | On failure, the reason: the drawing isn't part of this order or quote, isn't an image/svg+xml, or has been deleted. | --- # PUT /v1/orders/{orderId}/labels **Service:** Orders **Operation:** `OrderService_SetOrderLabels` The list you send becomes the order's complete label set: labels not listed are removed, and an empty list removes them all. Look up label ids with ListLabels. A label id that does not exist in your company is rejected with HTTP 404 (not_found) and nothing is changed. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order, as returned by CreateOrder. | ## Request body `SetOrderLabelsBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `labelIds` | string[] | no | The order's desired complete label set. An empty list removes every label. Duplicate ids are rejected. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `SetOrderLabelsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation — for example a badly formed or duplicated label id. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, the order is archived (archived orders are read-only), or the document is still a quote — label it with SetQuoteLabels. Also returned when a label id does not exist in your company: nothing is changed and the order's labels are left as they were. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `SetOrderLabelsResponse` | Field | Type | Description | | --- | --- | --- | | `labels` | Label[] | The order's labels after the change, oldest attachment first. | | `labels.labelId` | string | The label's id. | | `labels.name` | string | Display name of the label. Unique within your company. | | `labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | --- # POST /v1/orders/{orderId}/lines **Service:** Orders **Operation:** `OrderService_AddOrderLineItem` Build an order up over time: each call creates one line under the order and recomputes the order's totals. One line per call; add several lines with several calls. The new line's position among the existing lines is not controllable and is not guaranteed to be last; read the order for its final order. Adding a line does not change whether the order is submitted. Flashing lines cannot yet be added to an existing order and are rejected with HTTP 501 (not_implemented): include them when creating the order, or add them to the quote before conversion. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order to add the line item to, as returned by CreateOrder. | ## Request body `AddOrderLineItemBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `line` | SalesLine | yes | The line item to add: one of the supported line types (on-the-fly, catalogue, labour, notes, or product kit). | | `line.onTheFly` | OnTheFlyLine | no | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `line.onTheFly.productName` | string | yes | Name of the item. Required. | | `line.onTheFly.productDescription` | string | no | Optional longer description of the item. | | `line.onTheFly.colour` | string | no | Optional colour for the item. | | `line.onTheFly.pricing` | LinePricing | no | Pricing for this line. Unit price is required; the line total is required until server-side derivation is enabled for your account (see `totalPrice` on LinePricing), except on custom-formula lines, where it is always required. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.onTheFly.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.onTheFly.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.onTheFly.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.onTheFly.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.onTheFly.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.onTheFly.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.onTheFly.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.onTheFly.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.onTheFly.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.onTheFly.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.onTheFly.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.onTheFly.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.onTheFly.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.onTheFly.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.onTheFly.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.onTheFly.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.onTheFly.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.onTheFly.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.onTheFly.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.onTheFly.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.onTheFly.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. | | `line.catalogue` | CatalogueLine | no | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `line.catalogue.productId` | string | yes | The id of the catalogue product for this line. Required, and must match an existing catalogue product; an unknown id rejects the whole request. | | `line.catalogue.productRowId` | string | no | Optional id of a specific row within the catalogue product. It must be one of the product's rows. The row supplies the line's attributes, cost and unit price when you leave those out. | | `line.catalogue.rowPriceId` | string | no | Optional id selecting the row's price at one of your price levels — the `rowPriceId` returned by the catalogue (not the price level's own id). It must be a price of `productRowId`; sent without `productRowId`, it must be a price of one of the product's rows, and that row becomes the line's. When you send no `unitPrice`, the line is priced at this level. | | `line.catalogue.productName` | string | no | Display name for the line. Defaults to the product's name. | | `line.catalogue.productDescription` | string | no | Longer description for the line. | | `line.catalogue.categoryName` | string | no | Category name for the line. Defaults to the product's category. | | `line.catalogue.priceName` | string | no | Price name shown for the line. | | `line.catalogue.params` | object[] | no | Product parameters as a JSON array of {name, value} entries (for example [{"name": "Colour", "value": "Monument"}]). The colour is taken from the "Colour" parameter and validated against the product's available colours. | | `line.catalogue.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. Defaults to the row's attributes when the line names a row. | | `line.catalogue.pricing` | LinePricing | no | Pricing for this line. Parts you leave out come from the catalogue: `pricingStrategy` from the product; `cost` from the row; `unitPrice` from the row's price at `rowPriceId`, or the row's marked-up price (with the row's `markup`) when no price level is chosen. A line that names no row takes only the strategy, and `unitPrice` is yours to send. An omitted `isTaxFree` follows the customer and the product's tax-free setting (see `isTaxFree`). `quantity`, measurements and `totalPrice` follow the rules of every priced line. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.catalogue.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.catalogue.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.catalogue.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.catalogue.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.catalogue.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.catalogue.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.catalogue.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.catalogue.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.catalogue.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.catalogue.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.catalogue.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.catalogue.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.catalogue.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.catalogue.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.catalogue.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.catalogue.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.catalogue.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.catalogue.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.catalogue.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.catalogue.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.labour` | LabourLine | no | A line charging for labour. Set exactly one of the six line-type fields. | | `line.labour.labourUserId` | string | yes | The id of the worker who performed the labour. Required, and must be an active user in the company that owns the order or quote. | | `line.labour.productName` | string | no | A short name for the labour. | | `line.labour.productDescription` | string | no | A longer description of the labour. | | `line.labour.pricing` | LinePricing | no | Quantity and pricing for this line: the hours in `quantity`, the charge rate in `unitPrice` and the cost rate in `cost`. There are no rate overrides elsewhere on the line, and the worker's own rates are not applied: an omitted `cost` is stored as zero, not taken from the user's `hourlyRateCost`. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.labour.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.labour.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.labour.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.labour.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.labour.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.labour.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.labour.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.labour.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.labour.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.labour.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.labour.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.labour.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.labour.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.labour.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.labour.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.labour.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.labour.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.labour.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.labour.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.labour.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.notes` | NotesLine | no | A note line, with no pricing. Set exactly one of the six line-type fields. | | `line.notes.notesType` | NotesType | yes | The kind of note. Required. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `line.notes.notes` | string | no | The note text. | | `line.notes.productName` | string | no | A short name for the note line. | | `line.notes.productDescription` | string | no | A longer description for the note line. | | `line.flashing` | FlashingLine | no | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `line.flashing.templateId` | string | no | The id of the flashing material or template. Required when the order or quote is submitted. | | `line.flashing.productName` | string | no | The display name of the flashing material. | | `line.flashing.priceLevel` | string | no | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `line.flashing.colour` | string | no | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `line.flashing.thickness` | string | no | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `line.flashing.bends` | string | no | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `line.flashing.totalGirth` | string | no | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `line.flashing.totalLength` | string | no | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `line.flashing.unitPrice` | Money | no | The price per metre of the flashing. Required when the order or quote is submitted. | | `line.flashing.totalPrice` | Money | no | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `line.flashing.customPrice` | Money | no | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `line.flashing.customPricePerLength` | Money | no | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `line.flashing.subitems` | object[] | no | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `line.flashing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `line.flashing.drawing` | FlashingDrawing | yes | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `line.flashing.drawing.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `line.flashing.drawing.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `line.flashing.drawing.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `line.flashing.drawing.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `line.flashing.drawing.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `line.flashing.drawing.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `line.flashing.drawing.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `line.flashing.drawing.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `line.flashing.drawing.points.*.id` | string | no | The unique id of this point within the drawing. | | `line.flashing.drawing.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `line.flashing.drawing.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `line.flashing.drawing.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `line.flashing.drawing.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `line.flashing.drawing.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `line.flashing.drawing.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `line.flashing.drawing.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `line.flashing.drawing.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `line.flashing.drawing.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `line.flashing.drawing.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.drawing.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.drawing.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.drawing.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.drawing.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.drawing.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `line.flashing.drawing.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `line.flashing.drawing.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `line.flashing.drawing.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `line.flashing.drawing.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.drawing.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.drawing.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.drawing.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.drawing.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.drawing.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `line.flashing.drawing.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `line.flashing.drawing.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `line.flashing.drawing.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `line.flashing.drawing.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.drawing.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.drawing.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.drawing.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.drawing.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.drawing.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `line.flashing.drawing.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.drawing.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.drawing.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `line.flashing.drawing.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `line.flashing.drawing.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `line.flashing.drawing.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `line.flashing.drawing.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `line.flashing.drawing.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `line.flashing.drawing.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `line.flashing.drawing.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `line.flashing.drawing.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `line.flashing.drawing.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.drawing.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.drawing.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `line.flashing.drawing.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `line.flashing.drawing.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.drawing.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.drawing.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `line.flashing.otherSides` | FlashingDrawing[] | no | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `line.flashing.otherSides.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `line.flashing.otherSides.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `line.flashing.otherSides.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `line.flashing.otherSides.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `line.flashing.otherSides.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `line.flashing.otherSides.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `line.flashing.otherSides.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `line.flashing.otherSides.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `line.flashing.otherSides.points.*.id` | string | no | The unique id of this point within the drawing. | | `line.flashing.otherSides.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `line.flashing.otherSides.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `line.flashing.otherSides.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `line.flashing.otherSides.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `line.flashing.otherSides.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `line.flashing.otherSides.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `line.flashing.otherSides.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `line.flashing.otherSides.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `line.flashing.otherSides.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `line.flashing.otherSides.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.otherSides.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.otherSides.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.otherSides.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.otherSides.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.otherSides.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `line.flashing.otherSides.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `line.flashing.otherSides.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `line.flashing.otherSides.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `line.flashing.otherSides.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.otherSides.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.otherSides.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.otherSides.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.otherSides.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.otherSides.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `line.flashing.otherSides.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `line.flashing.otherSides.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `line.flashing.otherSides.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `line.flashing.otherSides.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.otherSides.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.otherSides.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.otherSides.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.otherSides.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.otherSides.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `line.flashing.otherSides.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.otherSides.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.otherSides.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `line.flashing.otherSides.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `line.flashing.otherSides.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `line.flashing.otherSides.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `line.flashing.otherSides.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `line.flashing.otherSides.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `line.flashing.otherSides.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `line.flashing.otherSides.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `line.flashing.otherSides.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `line.flashing.otherSides.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.otherSides.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.otherSides.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `line.flashing.otherSides.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `line.flashing.otherSides.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.otherSides.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.otherSides.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `line.flashing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.productKit` | ProductKitLine | no | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `line.productKit.kitId` | string | no | The kit's id (`kitId` on Kit, from the catalogue read surface). Optional: omit it to define an on-the-fly kit not linked to any catalogue kit — the kit is described entirely by this line's name, pricing, components, and sub-kits. | | `line.productKit.kitRowId` | string | no | The selected kit row/variant id. Optional. | | `line.productKit.productName` | string | no | Display name of the kit. | | `line.productKit.customPricing` | boolean | no | Whether the kit is custom-priced (true) rather than computed from its components (false). | | `line.productKit.description` | string | no | Free-text description of the kit. | | `line.productKit.displayKitItems` | object[] | no | Which documents and views show the kit's components, as a JSON array of display-target keys. The keys are "displayOnAllViews", "displayOnWorkOrderPdf", "displayOnDeliveryDocketPdf", "displayOnInvoicePdf", "displayOnQuotePdf", "displayOnProformaInvoicePdf", "displayOnCustomerOnlineView" and "displayOnAccountingPackage"; a key that is not one of these matches no document. Optional: when omitted, the kit takes your company's "Display individual Product Kit items on" setting, as a kit built in the Factory app does. An empty array shows the components on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `line.productKit.displaySubKits` | object[] | no | Which documents and views show the kit's sub-kits, as a JSON array of the same display-target keys as `displayKitItems`. Optional: when omitted, the kit takes your company's "Display Product Kit sub-kits on" setting, as a kit built in the Factory app does. An empty array shows the sub-kits on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `line.productKit.pricing` | LinePricing | no | Overall pricing for the kit line. Its `isTaxFree` is read before the components': a tax-free kit line exempts the whole kit whatever the components say, a taxable one lets each component's own `isTaxFree` decide, and a custom-priced kit is taxed on the kit line alone. Omit it and it depends on the kit: on a standard kit it is false, with the components following the customer, so a component you mark taxable is charged even for an exempt customer; on a custom-priced kit it follows the customer, so an exempt customer's custom kit is exempt in full. A value you send, true or false, is kept as sent. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.productKit.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.productKit.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.productKit.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.productKit.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.productKit.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.productKit.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.productKit.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.productKit.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.productKit.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.productKit.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.productKit.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.productKit.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.productKit.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.attributes` | object[] | no | Additional named attributes for the kit line, as a JSON array of {name, value} entries; for measurement-priced kits they describe the measurement entries. | | `line.productKit.components` | KitComponent[] | no | The kit's direct components. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `line.productKit.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `line.productKit.components.productId` | string | no | The catalogue product id for this component. Optional. | | `line.productKit.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `line.productKit.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `line.productKit.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `line.productKit.components.productDescription` | string | no | Free-text description of the component. | | `line.productKit.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `line.productKit.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `line.productKit.components.unitPrice` | Money | no | Price per unit. | | `line.productKit.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `line.productKit.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `line.productKit.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `line.productKit.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `line.productKit.components.cost` | Money | no | Cost of the component. | | `line.productKit.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `line.productKit.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `line.productKit.components.margin` | Money | no | Margin earned on the component. | | `line.productKit.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.productKit.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `line.productKit.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `line.productKit.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `line.productKit.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `line.productKit.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `line.productKit.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `line.productKit.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `line.productKit.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `line.productKit.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `line.productKit.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.subKits` | SubKit[] | no | The kit's sub-assemblies, each with its own components. A sub-assembly within a kit. A sub-kit groups a set of components under its own title and quantity, letting a kit be organized into named sub-assemblies. | | `line.productKit.subKits.title` | string | no | Display title of the sub-assembly. | | `line.productKit.subKits.quantity` | string | no | Quantity of this sub-assembly, as a decimal string. | | `line.productKit.subKits.actualQuantity` | string | no | Actual measured quantity, as a decimal string. | | `line.productKit.subKits.unitPrice` | Money | no | Price per unit of the sub-assembly. | | `line.productKit.subKits.totalPrice` | Money | no | Total price of the sub-assembly. | | `line.productKit.subKits.components` | KitComponent[] | no | The components that make up this sub-assembly. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `line.productKit.subKits.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `line.productKit.subKits.components.productId` | string | no | The catalogue product id for this component. Optional. | | `line.productKit.subKits.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `line.productKit.subKits.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `line.productKit.subKits.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `line.productKit.subKits.components.productDescription` | string | no | Free-text description of the component. | | `line.productKit.subKits.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `line.productKit.subKits.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `line.productKit.subKits.components.unitPrice` | Money | no | Price per unit. | | `line.productKit.subKits.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `line.productKit.subKits.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `line.productKit.subKits.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `line.productKit.subKits.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `line.productKit.subKits.components.cost` | Money | no | Cost of the component. | | `line.productKit.subKits.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `line.productKit.subKits.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `line.productKit.subKits.components.margin` | Money | no | Margin earned on the component. | | `line.productKit.subKits.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.productKit.subKits.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `line.productKit.subKits.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `line.productKit.subKits.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `line.productKit.subKits.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `line.productKit.subKits.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `line.productKit.subKits.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `line.productKit.subKits.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `line.productKit.subKits.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `line.productKit.subKits.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `line.productKit.subKits.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.subKits.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.subKits.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.subKits.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.subKits.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.subKits.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.subKits.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.subKits.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.id` | string | no | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `AddOrderLineItemResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation — the body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field) — or because the order is not modifiable: an invoiced or accounting-locked order refuses line changes (failed_precondition). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, the order is archived (archived orders are read-only), or the document is still a quote — manage its lines with the QuoteService line endpoints, swapping the id's order_ prefix for quote_ (the 26-character suffix stays the same). | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the order it updated), its outcome is still unknown, or the key was reused with a materially different body. Verify the order's lines with GetOrder, then send a new `requestId` to add a new line. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `AddOrderLineItemResponse` | Field | Type | Description | | --- | --- | --- | | `order` | Order | The order's id and recomputed totals (`subtotal`, `taxAmount`, `total`, `totalCost`, `margin`, `labourTotal`). No other field is populated — `lines` is empty and `isSubmitted` is absent regardless of the order's state — so read the order with GetOrder for the new line's id and position. | | `order.orderId` | string | The order's unique id. Read-only. | | `order.isSubmitted` | boolean | Whether the order has been submitted (true) or is still a draft (false). | | `order.customerId` | string | The id of the customer this order belongs to. Always present. Read-only. | | `order.customerCompanyName` | string | The customer's company name. Read-only. | | `order.statusId` | string | The id of the order's workflow status. Defaults to a "Submitted" status when the order is submitted without one set. | | `order.statusName` | string | The display name of the order's workflow status. Read-only. | | `order.subtotal` | Money | Order subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `order.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.taxAmount` | Money | Total tax for the order, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `order.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.total` | Money | Order grand total. Derived by the server; read-only. | | `order.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.margin` | Money | Order margin, computed by the server from the line items. Read-only. | | `order.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.totalCost` | Money | Total cost of the order, computed by the server from the line items. Read-only. | | `order.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.lines` | SalesLine[] | The order's line items. | | `order.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `order.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `order.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `order.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `order.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `order.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `order.lines.flashing.productName` | string | The display name of the flashing material. | | `order.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `order.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `order.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `order.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `order.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `order.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `order.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `order.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `order.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `order.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `order.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `order.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `order.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `order.adjustments` | Adjustment[] | Order-level fees, discounts, and markups. At most one fee per order is allowed. | | `order.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `order.adjustments.title` | string | A label describing the adjustment. | | `order.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `order.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `order.fulfilmentMethod` | FulfilmentMethod | How the customer receives the order: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `order.billingAddress` | Address | Billing address for the order. | | `order.billingAddress.address1` | string | First address line (street number and name). | | `order.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.billingAddress.city` | string | City or suburb. | | `order.billingAddress.state` | string | State, province, or region. | | `order.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `order.deliveryAddress.address1` | string | First address line (street number and name). | | `order.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.deliveryAddress.city` | string | City or suburb. | | `order.deliveryAddress.state` | string | State, province, or region. | | `order.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `order.installAddress.address1` | string | First address line (street number and name). | | `order.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.installAddress.city` | string | City or suburb. | | `order.installAddress.state` | string | State, province, or region. | | `order.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.reference` | string | Free-text external reference, e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. | | `order.orderNumber` | string | Factory's number for this order. Assigned by the server when the document was created (a quote keeps its number when it becomes an order), sequential within your company, and never changed. Shown as "Order #" in the Factory app (order list, order page, workflow board) and on the order confirmation, invoice, work order, and delivery docket, and used as the document reference in the accounting integrations. Read-only. | | `order.receivedStatus` | ReceivedStatus | Whether the order has been received. Read-only. Whether and how the order has reached the customer. Read-only. - RECEIVED_STATUS_UNSPECIFIED: Default, unset value. - RECEIVED_STATUS_DELIVERED: The order was delivered. - RECEIVED_STATUS_PICKED_UP: The order was picked up by the customer. - RECEIVED_STATUS_NOT_RECEIVED: The order has not yet reached the customer. - RECEIVED_STATUS_INSTALLED: The order was installed. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `order.paymentStatus` | PaymentStatus | The order's payment status. Read-only. Whether the order has been paid for. Read-only. - PAYMENT_STATUS_UNSPECIFIED: Default, unset value. - PAYMENT_STATUS_UNPAID: No payment has been received. - PAYMENT_STATUS_PARTIALLY_PAID: Part of the order total has been paid. - PAYMENT_STATUS_PAID: The order has been paid in full. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `order.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `order.createdAt` | string | When the order was created. Read-only. | | `order.submittedAt` | string | When the order was submitted. Read-only. | | `order.lastUpdatedAt` | string | When the order was last updated. Read-only. | | `order.requiredAt` | string | The date the order is required or needed by. | | `order.contact` | Contact | The order's point-of-contact person. | | `order.contact.name` | string | The contact person's name. | | `order.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `order.contact.phone` | string | The contact's landline phone number. | | `order.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `order.notes` | string | Free-text notes about the order. | | `order.pickupNotes` | string | Free-text notes shown when the order is picked up. | | `order.deliveryFee` | Money | The delivery fee charged on the order. Read-only. | | `order.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.createdByUserId` | string | The id of the user who created the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.submittedByUserId` | string | The id of the user who submitted the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.discountAmount` | Money | The order-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `order.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.invoiceNumber` | string | The order's invoice number as displayed: the order's own number, or the linked accounting system's number when synced. Empty until the order is invoiced. Read-only. | | `order.invoicedAt` | string | When the order was invoiced. Unset until the order is invoiced. Read-only. | | `order.statusColour` | string | The colour of the order's workflow status, for display (defaults to "transparent"). The display companion to `statusName`. Read-only. | | `order.tax` | TaxDetail | The tax descriptor for the order: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `order.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `order.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `order.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `order.labels` | Label[] | The labels attached to this order, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `order.labels.labelId` | string | The label's id. | | `order.labels.name` | string | Display name of the label. Unique within your company. | | `order.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `order.isArchived` | boolean | Whether the order is archived. Archived orders are hidden from every read by default: reach them with GetOrder's `includeArchived`, ListOrders' archived, or QueryOrders' `includeArchived`. Always false on default reads. Read-only. | | `order.labourTotal` | Money | The total charged for labour on the order: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `order.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # PUT /v1/orders/{orderId}/lines/{lineId} **Service:** Orders **Operation:** `OrderService_UpdateOrderLineItem` Send the full replacement line content; the whole line is replaced and the order's totals are recomputed. Identify the line by the server-assigned id returned when you read the order. A line id that does not belong to the order is rejected with HTTP 404 (not_found). An order that has been invoiced is rejected with HTTP 400 (failed_precondition). A flashing line cannot yet be replaced and is rejected with HTTP 501 (not_implemented). Concurrent writes to the same line are not detected: the last write wins. Re-read the order after writing if other clients may be editing it. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order the line belongs to, as returned by CreateOrder. | | `lineId` | path | string | yes | The server-assigned id of the line to replace, as returned when the order is read. This has a different form from the order id. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Request body `SalesLine` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `onTheFly` | OnTheFlyLine | no | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `onTheFly.productName` | string | yes | Name of the item. Required. | | `onTheFly.productDescription` | string | no | Optional longer description of the item. | | `onTheFly.colour` | string | no | Optional colour for the item. | | `onTheFly.pricing` | LinePricing | no | Pricing for this line. Unit price is required; the line total is required until server-side derivation is enabled for your account (see `totalPrice` on LinePricing), except on custom-formula lines, where it is always required. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `onTheFly.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `onTheFly.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `onTheFly.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `onTheFly.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `onTheFly.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `onTheFly.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `onTheFly.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `onTheFly.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `onTheFly.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `onTheFly.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `onTheFly.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `onTheFly.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `onTheFly.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `onTheFly.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `onTheFly.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `onTheFly.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `onTheFly.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `onTheFly.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `onTheFly.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `onTheFly.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `onTheFly.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. | | `catalogue` | CatalogueLine | no | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `catalogue.productId` | string | yes | The id of the catalogue product for this line. Required, and must match an existing catalogue product; an unknown id rejects the whole request. | | `catalogue.productRowId` | string | no | Optional id of a specific row within the catalogue product. It must be one of the product's rows. The row supplies the line's attributes, cost and unit price when you leave those out. | | `catalogue.rowPriceId` | string | no | Optional id selecting the row's price at one of your price levels — the `rowPriceId` returned by the catalogue (not the price level's own id). It must be a price of `productRowId`; sent without `productRowId`, it must be a price of one of the product's rows, and that row becomes the line's. When you send no `unitPrice`, the line is priced at this level. | | `catalogue.productName` | string | no | Display name for the line. Defaults to the product's name. | | `catalogue.productDescription` | string | no | Longer description for the line. | | `catalogue.categoryName` | string | no | Category name for the line. Defaults to the product's category. | | `catalogue.priceName` | string | no | Price name shown for the line. | | `catalogue.params` | object[] | no | Product parameters as a JSON array of {name, value} entries (for example [{"name": "Colour", "value": "Monument"}]). The colour is taken from the "Colour" parameter and validated against the product's available colours. | | `catalogue.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. Defaults to the row's attributes when the line names a row. | | `catalogue.pricing` | LinePricing | no | Pricing for this line. Parts you leave out come from the catalogue: `pricingStrategy` from the product; `cost` from the row; `unitPrice` from the row's price at `rowPriceId`, or the row's marked-up price (with the row's `markup`) when no price level is chosen. A line that names no row takes only the strategy, and `unitPrice` is yours to send. An omitted `isTaxFree` follows the customer and the product's tax-free setting (see `isTaxFree`). `quantity`, measurements and `totalPrice` follow the rules of every priced line. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `catalogue.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `catalogue.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `catalogue.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `catalogue.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `catalogue.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `catalogue.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `catalogue.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `catalogue.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `catalogue.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `catalogue.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `catalogue.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `catalogue.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `catalogue.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `catalogue.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `catalogue.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `catalogue.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `catalogue.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `catalogue.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `catalogue.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `catalogue.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `labour` | LabourLine | no | A line charging for labour. Set exactly one of the six line-type fields. | | `labour.labourUserId` | string | yes | The id of the worker who performed the labour. Required, and must be an active user in the company that owns the order or quote. | | `labour.productName` | string | no | A short name for the labour. | | `labour.productDescription` | string | no | A longer description of the labour. | | `labour.pricing` | LinePricing | no | Quantity and pricing for this line: the hours in `quantity`, the charge rate in `unitPrice` and the cost rate in `cost`. There are no rate overrides elsewhere on the line, and the worker's own rates are not applied: an omitted `cost` is stored as zero, not taken from the user's `hourlyRateCost`. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `labour.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `labour.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `labour.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `labour.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `labour.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `labour.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `labour.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `labour.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `labour.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `labour.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `labour.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `labour.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `labour.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `labour.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `labour.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `labour.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `labour.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `labour.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `labour.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `labour.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `notes` | NotesLine | no | A note line, with no pricing. Set exactly one of the six line-type fields. | | `notes.notesType` | NotesType | yes | The kind of note. Required. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `notes.notes` | string | no | The note text. | | `notes.productName` | string | no | A short name for the note line. | | `notes.productDescription` | string | no | A longer description for the note line. | | `flashing` | FlashingLine | no | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `flashing.templateId` | string | no | The id of the flashing material or template. Required when the order or quote is submitted. | | `flashing.productName` | string | no | The display name of the flashing material. | | `flashing.priceLevel` | string | no | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `flashing.colour` | string | no | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `flashing.thickness` | string | no | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `flashing.bends` | string | no | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `flashing.totalGirth` | string | no | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `flashing.totalLength` | string | no | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `flashing.unitPrice` | Money | no | The price per metre of the flashing. Required when the order or quote is submitted. | | `flashing.unitPrice.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.unitPrice.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.totalPrice` | Money | no | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `flashing.totalPrice.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.totalPrice.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.customPrice` | Money | no | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `flashing.customPrice.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.customPrice.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.customPricePerLength` | Money | no | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `flashing.customPricePerLength.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.customPricePerLength.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.subitems` | object[] | no | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `flashing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `flashing.drawing` | FlashingDrawing | yes | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `flashing.drawing.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `flashing.drawing.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `flashing.drawing.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `flashing.drawing.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `flashing.drawing.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `flashing.drawing.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `flashing.drawing.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `flashing.drawing.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `flashing.drawing.points.*.id` | string | no | The unique id of this point within the drawing. | | `flashing.drawing.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `flashing.drawing.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `flashing.drawing.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `flashing.drawing.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `flashing.drawing.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `flashing.drawing.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `flashing.drawing.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `flashing.drawing.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `flashing.drawing.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `flashing.drawing.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.drawing.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.drawing.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.drawing.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `flashing.drawing.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `flashing.drawing.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `flashing.drawing.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `flashing.drawing.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `flashing.drawing.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `flashing.drawing.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.drawing.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.drawing.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.drawing.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.drawing.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.drawing.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `flashing.drawing.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `flashing.drawing.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `flashing.drawing.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `flashing.drawing.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.drawing.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.drawing.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.drawing.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.drawing.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.drawing.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `flashing.drawing.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.drawing.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.drawing.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `flashing.drawing.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `flashing.drawing.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `flashing.drawing.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `flashing.drawing.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `flashing.drawing.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `flashing.drawing.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `flashing.drawing.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `flashing.drawing.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `flashing.drawing.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.drawing.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.drawing.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `flashing.drawing.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `flashing.drawing.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.drawing.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.drawing.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `flashing.otherSides` | FlashingDrawing[] | no | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `flashing.otherSides.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `flashing.otherSides.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `flashing.otherSides.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `flashing.otherSides.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `flashing.otherSides.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `flashing.otherSides.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `flashing.otherSides.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `flashing.otherSides.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `flashing.otherSides.points.*.id` | string | no | The unique id of this point within the drawing. | | `flashing.otherSides.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `flashing.otherSides.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `flashing.otherSides.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `flashing.otherSides.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `flashing.otherSides.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `flashing.otherSides.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `flashing.otherSides.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `flashing.otherSides.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `flashing.otherSides.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `flashing.otherSides.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.otherSides.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.otherSides.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.otherSides.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `flashing.otherSides.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `flashing.otherSides.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `flashing.otherSides.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `flashing.otherSides.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `flashing.otherSides.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `flashing.otherSides.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.otherSides.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.otherSides.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.otherSides.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.otherSides.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.otherSides.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `flashing.otherSides.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `flashing.otherSides.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `flashing.otherSides.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `flashing.otherSides.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.otherSides.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.otherSides.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.otherSides.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.otherSides.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.otherSides.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `flashing.otherSides.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.otherSides.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.otherSides.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `flashing.otherSides.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `flashing.otherSides.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `flashing.otherSides.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `flashing.otherSides.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `flashing.otherSides.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `flashing.otherSides.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `flashing.otherSides.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `flashing.otherSides.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `flashing.otherSides.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.otherSides.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.otherSides.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `flashing.otherSides.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `flashing.otherSides.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.otherSides.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.otherSides.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `flashing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `flashing.tax.rate` | string | no | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `flashing.tax.code` | string | no | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `flashing.tax.jurisdiction` | string | no | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `productKit` | ProductKitLine | no | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `productKit.kitId` | string | no | The kit's id (`kitId` on Kit, from the catalogue read surface). Optional: omit it to define an on-the-fly kit not linked to any catalogue kit — the kit is described entirely by this line's name, pricing, components, and sub-kits. | | `productKit.kitRowId` | string | no | The selected kit row/variant id. Optional. | | `productKit.productName` | string | no | Display name of the kit. | | `productKit.customPricing` | boolean | no | Whether the kit is custom-priced (true) rather than computed from its components (false). | | `productKit.description` | string | no | Free-text description of the kit. | | `productKit.displayKitItems` | object[] | no | Which documents and views show the kit's components, as a JSON array of display-target keys. The keys are "displayOnAllViews", "displayOnWorkOrderPdf", "displayOnDeliveryDocketPdf", "displayOnInvoicePdf", "displayOnQuotePdf", "displayOnProformaInvoicePdf", "displayOnCustomerOnlineView" and "displayOnAccountingPackage"; a key that is not one of these matches no document. Optional: when omitted, the kit takes your company's "Display individual Product Kit items on" setting, as a kit built in the Factory app does. An empty array shows the components on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `productKit.displaySubKits` | object[] | no | Which documents and views show the kit's sub-kits, as a JSON array of the same display-target keys as `displayKitItems`. Optional: when omitted, the kit takes your company's "Display Product Kit sub-kits on" setting, as a kit built in the Factory app does. An empty array shows the sub-kits on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `productKit.pricing` | LinePricing | no | Overall pricing for the kit line. Its `isTaxFree` is read before the components': a tax-free kit line exempts the whole kit whatever the components say, a taxable one lets each component's own `isTaxFree` decide, and a custom-priced kit is taxed on the kit line alone. Omit it and it depends on the kit: on a standard kit it is false, with the components following the customer, so a component you mark taxable is charged even for an exempt customer; on a custom-priced kit it follows the customer, so an exempt customer's custom kit is exempt in full. A value you send, true or false, is kept as sent. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `productKit.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `productKit.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `productKit.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `productKit.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `productKit.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `productKit.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `productKit.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `productKit.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `productKit.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `productKit.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `productKit.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `productKit.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `productKit.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.attributes` | object[] | no | Additional named attributes for the kit line, as a JSON array of {name, value} entries; for measurement-priced kits they describe the measurement entries. | | `productKit.components` | KitComponent[] | no | The kit's direct components. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `productKit.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `productKit.components.productId` | string | no | The catalogue product id for this component. Optional. | | `productKit.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `productKit.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `productKit.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `productKit.components.productDescription` | string | no | Free-text description of the component. | | `productKit.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `productKit.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `productKit.components.unitPrice` | Money | no | Price per unit. | | `productKit.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `productKit.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `productKit.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `productKit.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `productKit.components.cost` | Money | no | Cost of the component. | | `productKit.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `productKit.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `productKit.components.margin` | Money | no | Margin earned on the component. | | `productKit.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `productKit.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `productKit.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `productKit.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `productKit.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `productKit.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `productKit.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `productKit.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `productKit.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `productKit.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `productKit.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.subKits` | SubKit[] | no | The kit's sub-assemblies, each with its own components. A sub-assembly within a kit. A sub-kit groups a set of components under its own title and quantity, letting a kit be organized into named sub-assemblies. | | `productKit.subKits.title` | string | no | Display title of the sub-assembly. | | `productKit.subKits.quantity` | string | no | Quantity of this sub-assembly, as a decimal string. | | `productKit.subKits.actualQuantity` | string | no | Actual measured quantity, as a decimal string. | | `productKit.subKits.unitPrice` | Money | no | Price per unit of the sub-assembly. | | `productKit.subKits.totalPrice` | Money | no | Total price of the sub-assembly. | | `productKit.subKits.components` | KitComponent[] | no | The components that make up this sub-assembly. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `productKit.subKits.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `productKit.subKits.components.productId` | string | no | The catalogue product id for this component. Optional. | | `productKit.subKits.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `productKit.subKits.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `productKit.subKits.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `productKit.subKits.components.productDescription` | string | no | Free-text description of the component. | | `productKit.subKits.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `productKit.subKits.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `productKit.subKits.components.unitPrice` | Money | no | Price per unit. | | `productKit.subKits.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `productKit.subKits.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `productKit.subKits.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `productKit.subKits.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `productKit.subKits.components.cost` | Money | no | Cost of the component. | | `productKit.subKits.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `productKit.subKits.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `productKit.subKits.components.margin` | Money | no | Margin earned on the component. | | `productKit.subKits.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `productKit.subKits.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `productKit.subKits.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `productKit.subKits.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `productKit.subKits.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `productKit.subKits.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `productKit.subKits.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `productKit.subKits.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `productKit.subKits.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `productKit.subKits.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `productKit.subKits.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.subKits.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.subKits.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.subKits.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.subKits.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.subKits.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.subKits.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.subKits.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `id` | string | no | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `UpdateOrderLineItemResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation — the body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field) — or because the order is not modifiable: an invoiced or accounting-locked order refuses line changes (failed_precondition). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, the order is archived (archived orders are read-only), the document is still a quote — manage its lines with the QuoteService line endpoints — or the line id does not identify a line on this order. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `UpdateOrderLineItemResponse` | Field | Type | Description | | --- | --- | --- | | `order` | Order | The updated order, including the recomputed totals. | | `order.orderId` | string | The order's unique id. Read-only. | | `order.isSubmitted` | boolean | Whether the order has been submitted (true) or is still a draft (false). | | `order.customerId` | string | The id of the customer this order belongs to. Always present. Read-only. | | `order.customerCompanyName` | string | The customer's company name. Read-only. | | `order.statusId` | string | The id of the order's workflow status. Defaults to a "Submitted" status when the order is submitted without one set. | | `order.statusName` | string | The display name of the order's workflow status. Read-only. | | `order.subtotal` | Money | Order subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `order.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.taxAmount` | Money | Total tax for the order, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `order.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.total` | Money | Order grand total. Derived by the server; read-only. | | `order.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.margin` | Money | Order margin, computed by the server from the line items. Read-only. | | `order.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.totalCost` | Money | Total cost of the order, computed by the server from the line items. Read-only. | | `order.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.lines` | SalesLine[] | The order's line items. | | `order.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `order.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `order.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `order.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `order.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `order.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `order.lines.flashing.productName` | string | The display name of the flashing material. | | `order.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `order.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `order.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `order.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `order.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `order.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `order.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `order.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `order.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `order.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `order.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `order.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `order.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `order.adjustments` | Adjustment[] | Order-level fees, discounts, and markups. At most one fee per order is allowed. | | `order.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `order.adjustments.title` | string | A label describing the adjustment. | | `order.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `order.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `order.fulfilmentMethod` | FulfilmentMethod | How the customer receives the order: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `order.billingAddress` | Address | Billing address for the order. | | `order.billingAddress.address1` | string | First address line (street number and name). | | `order.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.billingAddress.city` | string | City or suburb. | | `order.billingAddress.state` | string | State, province, or region. | | `order.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `order.deliveryAddress.address1` | string | First address line (street number and name). | | `order.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.deliveryAddress.city` | string | City or suburb. | | `order.deliveryAddress.state` | string | State, province, or region. | | `order.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `order.installAddress.address1` | string | First address line (street number and name). | | `order.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.installAddress.city` | string | City or suburb. | | `order.installAddress.state` | string | State, province, or region. | | `order.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.reference` | string | Free-text external reference, e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. | | `order.orderNumber` | string | Factory's number for this order. Assigned by the server when the document was created (a quote keeps its number when it becomes an order), sequential within your company, and never changed. Shown as "Order #" in the Factory app (order list, order page, workflow board) and on the order confirmation, invoice, work order, and delivery docket, and used as the document reference in the accounting integrations. Read-only. | | `order.receivedStatus` | ReceivedStatus | Whether the order has been received. Read-only. Whether and how the order has reached the customer. Read-only. - RECEIVED_STATUS_UNSPECIFIED: Default, unset value. - RECEIVED_STATUS_DELIVERED: The order was delivered. - RECEIVED_STATUS_PICKED_UP: The order was picked up by the customer. - RECEIVED_STATUS_NOT_RECEIVED: The order has not yet reached the customer. - RECEIVED_STATUS_INSTALLED: The order was installed. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `order.paymentStatus` | PaymentStatus | The order's payment status. Read-only. Whether the order has been paid for. Read-only. - PAYMENT_STATUS_UNSPECIFIED: Default, unset value. - PAYMENT_STATUS_UNPAID: No payment has been received. - PAYMENT_STATUS_PARTIALLY_PAID: Part of the order total has been paid. - PAYMENT_STATUS_PAID: The order has been paid in full. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `order.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `order.createdAt` | string | When the order was created. Read-only. | | `order.submittedAt` | string | When the order was submitted. Read-only. | | `order.lastUpdatedAt` | string | When the order was last updated. Read-only. | | `order.requiredAt` | string | The date the order is required or needed by. | | `order.contact` | Contact | The order's point-of-contact person. | | `order.contact.name` | string | The contact person's name. | | `order.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `order.contact.phone` | string | The contact's landline phone number. | | `order.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `order.notes` | string | Free-text notes about the order. | | `order.pickupNotes` | string | Free-text notes shown when the order is picked up. | | `order.deliveryFee` | Money | The delivery fee charged on the order. Read-only. | | `order.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.createdByUserId` | string | The id of the user who created the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.submittedByUserId` | string | The id of the user who submitted the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.discountAmount` | Money | The order-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `order.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.invoiceNumber` | string | The order's invoice number as displayed: the order's own number, or the linked accounting system's number when synced. Empty until the order is invoiced. Read-only. | | `order.invoicedAt` | string | When the order was invoiced. Unset until the order is invoiced. Read-only. | | `order.statusColour` | string | The colour of the order's workflow status, for display (defaults to "transparent"). The display companion to `statusName`. Read-only. | | `order.tax` | TaxDetail | The tax descriptor for the order: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `order.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `order.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `order.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `order.labels` | Label[] | The labels attached to this order, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `order.labels.labelId` | string | The label's id. | | `order.labels.name` | string | Display name of the label. Unique within your company. | | `order.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `order.isArchived` | boolean | Whether the order is archived. Archived orders are hidden from every read by default: reach them with GetOrder's `includeArchived`, ListOrders' archived, or QueryOrders' `includeArchived`. Always false on default reads. Read-only. | | `order.labourTotal` | Money | The total charged for labour on the order: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `order.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # DELETE /v1/orders/{orderId}/lines/{lineId} **Service:** Orders **Operation:** `OrderService_DeleteOrderLineItem` Identify the line by the server-assigned id returned when you read the order; the order's totals are recomputed after removal. A line id that does not belong to the order is rejected with HTTP 404 (not_found). An order that has been invoiced is rejected with HTTP 400 (failed_precondition). A flashing line cannot yet be removed and is rejected with HTTP 501 (not_implemented). Concurrent writes to the same line are not detected: the last write wins. Re-read the order after writing if other clients may be editing it. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order the line belongs to, as returned by CreateOrder. | | `lineId` | path | string | yes | The server-assigned id of the line to remove, as returned when the order is read. This has a different form from the order id. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `DeleteOrderLineItemResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id (the body is a validation-failure envelope: an overall type and message and one entry per problem, each with a stable code, a message, and a param pointing at the offending parameter) — or because the order is not modifiable: an invoiced or accounting-locked order refuses line changes (failed_precondition). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, the order is archived (archived orders are read-only), the document is still a quote — manage its lines with the QuoteService line endpoints — or the line id does not identify a line on this order. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `DeleteOrderLineItemResponse` | Field | Type | Description | | --- | --- | --- | | `order` | Order | The updated order, with the line removed and totals recomputed. | | `order.orderId` | string | The order's unique id. Read-only. | | `order.isSubmitted` | boolean | Whether the order has been submitted (true) or is still a draft (false). | | `order.customerId` | string | The id of the customer this order belongs to. Always present. Read-only. | | `order.customerCompanyName` | string | The customer's company name. Read-only. | | `order.statusId` | string | The id of the order's workflow status. Defaults to a "Submitted" status when the order is submitted without one set. | | `order.statusName` | string | The display name of the order's workflow status. Read-only. | | `order.subtotal` | Money | Order subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `order.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.taxAmount` | Money | Total tax for the order, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `order.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.total` | Money | Order grand total. Derived by the server; read-only. | | `order.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.margin` | Money | Order margin, computed by the server from the line items. Read-only. | | `order.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.totalCost` | Money | Total cost of the order, computed by the server from the line items. Read-only. | | `order.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.lines` | SalesLine[] | The order's line items. | | `order.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `order.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `order.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `order.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `order.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `order.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `order.lines.flashing.productName` | string | The display name of the flashing material. | | `order.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `order.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `order.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `order.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `order.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `order.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `order.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `order.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `order.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `order.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `order.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `order.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `order.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `order.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `order.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `order.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `order.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `order.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `order.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `order.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `order.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `order.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `order.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `order.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `order.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `order.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `order.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `order.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `order.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `order.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `order.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `order.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `order.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `order.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `order.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `order.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `order.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `order.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `order.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `order.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `order.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `order.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `order.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `order.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `order.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `order.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `order.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `order.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `order.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `order.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `order.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `order.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `order.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `order.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `order.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `order.adjustments` | Adjustment[] | Order-level fees, discounts, and markups. At most one fee per order is allowed. | | `order.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `order.adjustments.title` | string | A label describing the adjustment. | | `order.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `order.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `order.fulfilmentMethod` | FulfilmentMethod | How the customer receives the order: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `order.billingAddress` | Address | Billing address for the order. | | `order.billingAddress.address1` | string | First address line (street number and name). | | `order.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.billingAddress.city` | string | City or suburb. | | `order.billingAddress.state` | string | State, province, or region. | | `order.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `order.deliveryAddress.address1` | string | First address line (street number and name). | | `order.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.deliveryAddress.city` | string | City or suburb. | | `order.deliveryAddress.state` | string | State, province, or region. | | `order.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `order.installAddress.address1` | string | First address line (street number and name). | | `order.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `order.installAddress.city` | string | City or suburb. | | `order.installAddress.state` | string | State, province, or region. | | `order.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `order.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `order.reference` | string | Free-text external reference, e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. | | `order.orderNumber` | string | Factory's number for this order. Assigned by the server when the document was created (a quote keeps its number when it becomes an order), sequential within your company, and never changed. Shown as "Order #" in the Factory app (order list, order page, workflow board) and on the order confirmation, invoice, work order, and delivery docket, and used as the document reference in the accounting integrations. Read-only. | | `order.receivedStatus` | ReceivedStatus | Whether the order has been received. Read-only. Whether and how the order has reached the customer. Read-only. - RECEIVED_STATUS_UNSPECIFIED: Default, unset value. - RECEIVED_STATUS_DELIVERED: The order was delivered. - RECEIVED_STATUS_PICKED_UP: The order was picked up by the customer. - RECEIVED_STATUS_NOT_RECEIVED: The order has not yet reached the customer. - RECEIVED_STATUS_INSTALLED: The order was installed. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `order.paymentStatus` | PaymentStatus | The order's payment status. Read-only. Whether the order has been paid for. Read-only. - PAYMENT_STATUS_UNSPECIFIED: Default, unset value. - PAYMENT_STATUS_UNPAID: No payment has been received. - PAYMENT_STATUS_PARTIALLY_PAID: Part of the order total has been paid. - PAYMENT_STATUS_PAID: The order has been paid in full. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `order.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `order.createdAt` | string | When the order was created. Read-only. | | `order.submittedAt` | string | When the order was submitted. Read-only. | | `order.lastUpdatedAt` | string | When the order was last updated. Read-only. | | `order.requiredAt` | string | The date the order is required or needed by. | | `order.contact` | Contact | The order's point-of-contact person. | | `order.contact.name` | string | The contact person's name. | | `order.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `order.contact.phone` | string | The contact's landline phone number. | | `order.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `order.notes` | string | Free-text notes about the order. | | `order.pickupNotes` | string | Free-text notes shown when the order is picked up. | | `order.deliveryFee` | Money | The delivery fee charged on the order. Read-only. | | `order.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.createdByUserId` | string | The id of the user who created the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.submittedByUserId` | string | The id of the user who submitted the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `order.discountAmount` | Money | The order-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `order.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `order.invoiceNumber` | string | The order's invoice number as displayed: the order's own number, or the linked accounting system's number when synced. Empty until the order is invoiced. Read-only. | | `order.invoicedAt` | string | When the order was invoiced. Unset until the order is invoiced. Read-only. | | `order.statusColour` | string | The colour of the order's workflow status, for display (defaults to "transparent"). The display companion to `statusName`. Read-only. | | `order.tax` | TaxDetail | The tax descriptor for the order: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `order.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `order.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `order.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `order.labels` | Label[] | The labels attached to this order, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `order.labels.labelId` | string | The label's id. | | `order.labels.name` | string | Display name of the label. Unique within your company. | | `order.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `order.isArchived` | boolean | Whether the order is archived. Archived orders are hidden from every read by default: reach them with GetOrder's `includeArchived`, ListOrders' archived, or QueryOrders' `includeArchived`. Always false on default reads. Read-only. | | `order.labourTotal` | Money | The total charged for labour on the order: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `order.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `order.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # GET /v1/orders/{orderId}/messages **Service:** Orders **Operation:** `OrderService_ListMessages` Returns the order's conversation — the same thread shown in the Factory app's Collaborate tab — oldest message first: text written by your team, files they attached, and documents the app generated. Each message carries its attachments, each with a presigned download URL. Most attachments are posted without any text, so expect many file-only messages. Signature captures collected in the app are not returned. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order whose conversation to list. Required. | | `pageSize` | query | integer | no | Maximum number of messages to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `salesordersListMessagesResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, or the document is still a quote — read its conversation with the QuoteService endpoint, swapping the id's order_ prefix for quote_ (the 26-character suffix stays the same). | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `salesordersListMessagesResponse` | Field | Type | Description | | --- | --- | --- | | `messages` | Message[] | The messages in this page, oldest first. | | `messages.id` | string | The id of this message. | | `messages.authorName` | string | The display name of the person who wrote the message. Empty when the author's account no longer exists. | | `messages.authorEmail` | string | The email address of the person who wrote the message. Empty when the author's account no longer exists. | | `messages.text` | string | The message text, as an HTML fragment. Messages written in the Factory app carry the app's markup (including @-mentions); plain text posted through this API is HTML-escaped and `

`-wrapped on write, and reads back in that stored form. Empty for file-only messages — most attachments are posted without any text. | | `messages.createdAt` | string | When the message was posted. | | `messages.attachments` | Attachment[] | The files attached to this message, if any. | | `messages.attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `messages.attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `messages.attachments.filename` | string | The file's name, as uploaded. | | `messages.attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `messages.attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `messages.attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `messages.attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `messages.attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `messages.attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/orders/{orderId}/messages **Service:** Orders **Operation:** `OrderService_PostMessage` Adds a plain-text message to the order's conversation, visible immediately in the Factory app's Collaborate tab. The message is stored as an HTML fragment — the posted text HTML-escaped and `

`-wrapped — and reads back in that form. There is no way to @-mention a user through this API: because text is escaped, mention markup renders as literal text and notifies no one. PostMessage sends text on its own; to send a file with text, use the caption on UploadAttachment. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `orderId` | path | string | yes | The id of the order to post to. Required. | ## Request body `OrderServicePostMessageBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `text` | string | yes | The message text, as plain text. Required. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `salesordersPostMessageResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No order with the given id exists for the authenticated company, or the document is still a quote — post to the QuoteService endpoint, swapping the id's order_ prefix for quote_ (the 26-character suffix stays the same). | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the message it posted), its outcome is still unknown, or the key was reused with a materially different body. Verify the conversation with ListMessages, then send a new `requestId` to post a new message. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `salesordersPostMessageResponse` | Field | Type | Description | | --- | --- | --- | | `message` | Message | The created message. | | `message.id` | string | The id of this message. | | `message.authorName` | string | The display name of the person who wrote the message. Empty when the author's account no longer exists. | | `message.authorEmail` | string | The email address of the person who wrote the message. Empty when the author's account no longer exists. | | `message.text` | string | The message text, as an HTML fragment. Messages written in the Factory app carry the app's markup (including @-mentions); plain text posted through this API is HTML-escaped and `

`-wrapped on write, and reads back in that stored form. Empty for file-only messages — most attachments are posted without any text. | | `message.createdAt` | string | When the message was posted. | | `message.attachments` | Attachment[] | The files attached to this message, if any. | | `message.attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `message.attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `message.attachments.filename` | string | The file's name, as uploaded. | | `message.attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `message.attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `message.attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `message.attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `message.attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `message.attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | --- # GET /v1/orders:query **Service:** Orders **Operation:** `OrderService_QueryOrders` Like ListOrders, but ordered by when each order last changed (oldest first) and filterable by an `updatedSince` / `updatedBefore` time window — so a poller fetches only what changed since its last run and checkpoints on the most recent order it saw. Use ListOrders for simple lookups; use this to keep an external system (CRM, reporting, accounting) in sync. Paginated via `pageSize` / `pageToken`. Carries confirmed orders only — quotes are read through QuoteService, and work-in-progress documents never appear. One migration to expect while syncing: an order that began as a quote enters this feed at conversion as an order id that shares the quote id's 26-character suffix — "quote_" becomes "order_" — so a document you first saw on the quote feed is recognised here by swapping the prefix. The same three status dimensions as ListOrders are filterable here: * Workflow status (`statusId` or `statusName`) — the order's position on your company's kanban board. Discover statuses with GET /v1/company/order-statuses, then filter by id or exact name. * Payment status (`paymentStatus`) — unpaid, partially paid, or paid. * Received status (`receivedStatus`) — delivered, picked up, installed, or not yet received. Additional filters: `customerId`, reference (substring), `isSubmitted`, `fulfilmentMethod`, `createdAfter`/`createdBefore` (creation window), `requiredAfter`/`requiredBefore` (required-by window), and `includeArchived`. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `updatedSince` | query | string | no | Return only orders updated at or after this time. Pass your last sync time here to fetch just what changed since then; omit to start from the beginning. This is the primary filter for incremental sync. | | `updatedBefore` | query | string | no | Optional upper bound: return only orders updated strictly before this time. Combine with `updatedSince` to page through a bounded window; omit for "up to now". | | `pageSize` | query | integer | no | Maximum number of orders to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `customerId` | query | string | no | Optional: only return orders for this customer id. | | `reference` | query | string | no | Optional: only return orders whose external reference contains this text, compared case-insensitively (a substring match, not exact-equals). | | `statusId` | query | string | no | Optional: only return orders whose current workflow status matches this id. Workflow statuses are company-configured kanban columns — every company has different statuses. Call GET /v1/company/order-statuses to discover your company's statuses and their ids. Use either `statusId` or `statusName` to filter, not both. | | `paymentStatus` | query | string | no | Optional: only return orders with this payment status. Unlike workflow status, payment status is a fixed set: UNPAID, PARTIALLY_PAID, or PAID. Leave unset to include all. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `isSubmitted` | query | boolean | no | Optional: filter by whether the order has been submitted. Leave unset to return all orders; set to false to return only drafts; set to true to return only submitted orders. | | `includeArchived` | query | boolean | no | Optional: widen the feed to include archived orders. False (the default) keeps today's feed of live orders; true returns live AND archived orders in one feed. Archiving stamps the order's update time, so with this flag a poller sees the archive happen in-band: the order reappears with `isArchived`: true and a fresh `lastUpdatedAt` (and a restore reappears with false) — no separate feed to reconcile. Changing this flag mid-pagination invalidates the page token. | | `createdAfter` | query | string | no | Only orders created at or after this time (inclusive). Optional; filters on the order's creation time, independent of the updated window — combine the two windows to sync recent changes for orders created in a period. Note: this parameter is named `createdAfter`, not `createdSince` — it is a range filter, not a sync checkpoint. | | `createdBefore` | query | string | no | Only orders created strictly before this time (exclusive). Optional; combine with `createdAfter` to bound a creation window. | | `statusName` | query | string | no | Optional: only return orders whose current workflow status has this exact name (case-sensitive). An alternative to `statusId` when you know the name but not the id — for example `statusName=Submitted`. Call GET /v1/company/order-statuses to see available names. | | `receivedStatus` | query | string | no | Optional: only return orders with this received status. Unlike workflow status, received status is a fixed set: DELIVERED, PICKED_UP, NOT_RECEIVED, or INSTALLED. Leave unset to include all. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `fulfilmentMethod` | query | string | no | Optional: only return orders with this fulfilment method. A fixed set: DELIVER, PICK_UP, or INSTALL. Leave unset to include all. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `requiredAfter` | query | string | no | Only orders required at or after this time. Optional; filters on the order's required-by date, independent of the other filters. | | `requiredBefore` | query | string | no | Only orders required strictly before this time. Optional; combine with `requiredAfter` to bound a required-by window. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `QueryOrdersResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `QueryOrdersResponse` | Field | Type | Description | | --- | --- | --- | | `orders` | Order[] | The orders in this page, oldest change first. Use the last order's update time as the `updatedSince` for your next call. | | `orders.orderId` | string | The order's unique id. Read-only. | | `orders.isSubmitted` | boolean | Whether the order has been submitted (true) or is still a draft (false). | | `orders.customerId` | string | The id of the customer this order belongs to. Always present. Read-only. | | `orders.customerCompanyName` | string | The customer's company name. Read-only. | | `orders.statusId` | string | The id of the order's workflow status. Defaults to a "Submitted" status when the order is submitted without one set. | | `orders.statusName` | string | The display name of the order's workflow status. Read-only. | | `orders.subtotal` | Money | Order subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `orders.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.taxAmount` | Money | Total tax for the order, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `orders.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.total` | Money | Order grand total. Derived by the server; read-only. | | `orders.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.margin` | Money | Order margin, computed by the server from the line items. Read-only. | | `orders.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.totalCost` | Money | Total cost of the order, computed by the server from the line items. Read-only. | | `orders.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.lines` | SalesLine[] | The order's line items. | | `orders.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `orders.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `orders.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `orders.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `orders.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `orders.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `orders.lines.flashing.productName` | string | The display name of the flashing material. | | `orders.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `orders.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `orders.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `orders.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `orders.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `orders.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `orders.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `orders.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `orders.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `orders.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `orders.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `orders.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `orders.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `orders.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `orders.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `orders.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `orders.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `orders.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `orders.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `orders.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `orders.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `orders.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `orders.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `orders.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `orders.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `orders.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `orders.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `orders.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `orders.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `orders.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `orders.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `orders.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `orders.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `orders.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `orders.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `orders.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `orders.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `orders.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `orders.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `orders.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `orders.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `orders.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `orders.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `orders.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `orders.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `orders.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `orders.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `orders.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `orders.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `orders.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `orders.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `orders.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `orders.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `orders.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `orders.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `orders.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `orders.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `orders.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `orders.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `orders.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `orders.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `orders.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `orders.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `orders.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `orders.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `orders.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `orders.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `orders.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `orders.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `orders.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `orders.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `orders.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `orders.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `orders.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `orders.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `orders.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `orders.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `orders.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `orders.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `orders.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `orders.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `orders.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `orders.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `orders.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `orders.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `orders.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `orders.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `orders.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `orders.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `orders.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `orders.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `orders.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `orders.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `orders.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `orders.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `orders.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `orders.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `orders.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `orders.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `orders.adjustments` | Adjustment[] | Order-level fees, discounts, and markups. At most one fee per order is allowed. | | `orders.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `orders.adjustments.title` | string | A label describing the adjustment. | | `orders.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `orders.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `orders.fulfilmentMethod` | FulfilmentMethod | How the customer receives the order: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `orders.billingAddress` | Address | Billing address for the order. | | `orders.billingAddress.address1` | string | First address line (street number and name). | | `orders.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `orders.billingAddress.city` | string | City or suburb. | | `orders.billingAddress.state` | string | State, province, or region. | | `orders.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `orders.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `orders.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `orders.deliveryAddress.address1` | string | First address line (street number and name). | | `orders.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `orders.deliveryAddress.city` | string | City or suburb. | | `orders.deliveryAddress.state` | string | State, province, or region. | | `orders.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `orders.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `orders.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `orders.installAddress.address1` | string | First address line (street number and name). | | `orders.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `orders.installAddress.city` | string | City or suburb. | | `orders.installAddress.state` | string | State, province, or region. | | `orders.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `orders.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `orders.reference` | string | Free-text external reference, e.g. the order id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the order documents. | | `orders.orderNumber` | string | Factory's number for this order. Assigned by the server when the document was created (a quote keeps its number when it becomes an order), sequential within your company, and never changed. Shown as "Order #" in the Factory app (order list, order page, workflow board) and on the order confirmation, invoice, work order, and delivery docket, and used as the document reference in the accounting integrations. Read-only. | | `orders.receivedStatus` | ReceivedStatus | Whether the order has been received. Read-only. Whether and how the order has reached the customer. Read-only. - RECEIVED_STATUS_UNSPECIFIED: Default, unset value. - RECEIVED_STATUS_DELIVERED: The order was delivered. - RECEIVED_STATUS_PICKED_UP: The order was picked up by the customer. - RECEIVED_STATUS_NOT_RECEIVED: The order has not yet reached the customer. - RECEIVED_STATUS_INSTALLED: The order was installed. One of: RECEIVED_STATUS_UNSPECIFIED, RECEIVED_STATUS_DELIVERED, RECEIVED_STATUS_PICKED_UP, RECEIVED_STATUS_NOT_RECEIVED, RECEIVED_STATUS_INSTALLED. | | `orders.paymentStatus` | PaymentStatus | The order's payment status. Read-only. Whether the order has been paid for. Read-only. - PAYMENT_STATUS_UNSPECIFIED: Default, unset value. - PAYMENT_STATUS_UNPAID: No payment has been received. - PAYMENT_STATUS_PARTIALLY_PAID: Part of the order total has been paid. - PAYMENT_STATUS_PAID: The order has been paid in full. One of: PAYMENT_STATUS_UNSPECIFIED, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PARTIALLY_PAID, PAYMENT_STATUS_PAID. | | `orders.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `orders.createdAt` | string | When the order was created. Read-only. | | `orders.submittedAt` | string | When the order was submitted. Read-only. | | `orders.lastUpdatedAt` | string | When the order was last updated. Read-only. | | `orders.requiredAt` | string | The date the order is required or needed by. | | `orders.contact` | Contact | The order's point-of-contact person. | | `orders.contact.name` | string | The contact person's name. | | `orders.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `orders.contact.phone` | string | The contact's landline phone number. | | `orders.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `orders.notes` | string | Free-text notes about the order. | | `orders.pickupNotes` | string | Free-text notes shown when the order is picked up. | | `orders.deliveryFee` | Money | The delivery fee charged on the order. Read-only. | | `orders.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.createdByUserId` | string | The id of the user who created the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `orders.submittedByUserId` | string | The id of the user who submitted the order, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `orders.discountAmount` | Money | The order-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `orders.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `orders.invoiceNumber` | string | The order's invoice number as displayed: the order's own number, or the linked accounting system's number when synced. Empty until the order is invoiced. Read-only. | | `orders.invoicedAt` | string | When the order was invoiced. Unset until the order is invoiced. Read-only. | | `orders.statusColour` | string | The colour of the order's workflow status, for display (defaults to "transparent"). The display companion to `statusName`. Read-only. | | `orders.tax` | TaxDetail | The tax descriptor for the order: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `orders.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `orders.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `orders.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `orders.labels` | Label[] | The labels attached to this order, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `orders.labels.labelId` | string | The label's id. | | `orders.labels.name` | string | Display name of the label. Unique within your company. | | `orders.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `orders.isArchived` | boolean | Whether the order is archived. Archived orders are hidden from every read by default: reach them with GetOrder's `includeArchived`, ListOrders' archived, or QueryOrders' `includeArchived`. Always false on default reads. Read-only. | | `orders.labourTotal` | Money | The total charged for labour on the order: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `orders.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `orders.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/quotes **Service:** Quotes **Operation:** `QuoteService_ListQuotes` A flat, company-scoped list of quotes. Quotes only — confirmed orders are read through OrderService. Quotes are returned most recently updated first; because that order shifts as quotes change, use QueryQuotes for a stable ordering suitable for incremental sync. List entries are summaries — the quote header and its totals without the line items; fetch the full document with GetQuote. Paginated via `pageSize` / `pageToken`. Quotes have no workflow or payment status. Available filters: `customerId`, reference (substring), `isSubmitted` (draft/sent), `labelIds`, `fulfilmentMethod`, `requiredAfter`/`requiredBefore` (required-by window), and archived (live vs archived view). ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of quotes to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `customerId` | query | string | no | Optional: only return quotes for this customer id. | | `reference` | query | string | no | Optional: only return quotes whose external reference contains this text, compared case-insensitively (a substring match, not exact-equals) — e.g. to find a previously submitted quote by part of its reference. | | `isSubmitted` | query | boolean | no | Optional: filter by whether the quote has been sent. Leave unset to return all quotes; set to false to return only drafts; set to true to return only sent quotes. | | `labelIds` | query | array | no | Optional: only return quotes carrying at least one of these labels (by id — discover your labels with GET /v1/company/labels). An id not in your label set matches no quotes. Combines with the other filters. Changing the label filter mid-pagination invalidates the page token. Duplicate ids are rejected. | | `fulfilmentMethod` | query | string | no | Optional: only return quotes with this fulfilment method. Leave unset to include all. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `requiredAfter` | query | string | no | Only quotes required at or after this time. Optional; filters on the quote's required-by date, independent of the other filters. | | `requiredBefore` | query | string | no | Only quotes required strictly before this time. Optional; combine with `requiredAfter` to bound a required-by window. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListQuotesResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListQuotesResponse` | Field | Type | Description | | --- | --- | --- | | `quotes` | Quote[] | The quotes in this page. | | `quotes.quoteId` | string | The quote's unique id. Read-only. | | `quotes.isSubmitted` | boolean | Whether the quote has been finalised and sent (true) or is still a draft (false). | | `quotes.customerId` | string | The id of the customer this quote belongs to. Always present. Read-only. | | `quotes.customerCompanyName` | string | The customer's company name. Read-only. | | `quotes.subtotal` | Money | Quote subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `quotes.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.taxAmount` | Money | Total tax for the quote, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `quotes.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.total` | Money | Quote grand total. Derived by the server; read-only. | | `quotes.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.margin` | Money | Quote margin, computed by the server from the line items. Read-only. | | `quotes.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.totalCost` | Money | Total cost of the quote, computed by the server from the line items. Read-only. | | `quotes.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.discountAmount` | Money | The quote-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `quotes.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.lines` | SalesLine[] | The quote's line items. | | `quotes.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `quotes.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `quotes.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `quotes.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `quotes.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `quotes.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `quotes.lines.flashing.productName` | string | The display name of the flashing material. | | `quotes.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `quotes.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `quotes.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `quotes.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quotes.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quotes.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `quotes.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `quotes.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `quotes.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `quotes.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `quotes.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `quotes.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `quotes.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quotes.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quotes.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quotes.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quotes.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quotes.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quotes.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quotes.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `quotes.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quotes.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `quotes.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quotes.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quotes.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quotes.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quotes.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quotes.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quotes.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quotes.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quotes.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quotes.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quotes.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quotes.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quotes.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quotes.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quotes.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quotes.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quotes.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quotes.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quotes.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quotes.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quotes.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quotes.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quotes.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quotes.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `quotes.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quotes.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quotes.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quotes.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quotes.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quotes.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quotes.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quotes.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quotes.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quotes.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quotes.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quotes.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quotes.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quotes.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `quotes.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quotes.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `quotes.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quotes.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quotes.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quotes.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quotes.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quotes.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quotes.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quotes.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quotes.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quotes.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quotes.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quotes.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quotes.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quotes.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quotes.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quotes.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quotes.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quotes.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quotes.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quotes.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quotes.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quotes.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `quotes.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quotes.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quotes.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quotes.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quotes.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quotes.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quotes.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `quotes.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `quotes.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `quotes.adjustments` | Adjustment[] | Quote-level fees, discounts, and markups. At most one fee per quote is allowed. | | `quotes.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `quotes.adjustments.title` | string | A label describing the adjustment. | | `quotes.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `quotes.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `quotes.fulfilmentMethod` | FulfilmentMethod | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `quotes.billingAddress` | Address | Billing address for the quote. | | `quotes.billingAddress.address1` | string | First address line (street number and name). | | `quotes.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `quotes.billingAddress.city` | string | City or suburb. | | `quotes.billingAddress.state` | string | State, province, or region. | | `quotes.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quotes.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quotes.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `quotes.deliveryAddress.address1` | string | First address line (street number and name). | | `quotes.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `quotes.deliveryAddress.city` | string | City or suburb. | | `quotes.deliveryAddress.state` | string | State, province, or region. | | `quotes.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quotes.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quotes.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `quotes.installAddress.address1` | string | First address line (street number and name). | | `quotes.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `quotes.installAddress.city` | string | City or suburb. | | `quotes.installAddress.state` | string | State, province, or region. | | `quotes.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quotes.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quotes.deliveryFee` | Money | The delivery fee quoted on the order. Read-only. | | `quotes.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.reference` | string | Free-text external reference, e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. | | `quotes.orderNumber` | string | Factory's number for this quote. Assigned by the server when the quote is created, sequential within your company, and never changed — the quote keeps this number if it becomes an order. Shown as "Quote #" on the quote PDF and email, as "Order #" in the Factory app (order list, order page, workflow board) and on invoices and delivery documents, and used as the document reference in the accounting integrations. Read-only. | | `quotes.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `quotes.contact` | Contact | The quote's point-of-contact person. | | `quotes.contact.name` | string | The contact person's name. | | `quotes.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `quotes.contact.phone` | string | The contact's landline phone number. | | `quotes.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `quotes.notes` | string | Free-text notes about the quote. | | `quotes.requiredAt` | string | The date the customer needs the order by, if the quote is accepted. | | `quotes.quotedAt` | string | The date the quote was issued. | | `quotes.createdAt` | string | When the quote was created. Read-only. | | `quotes.submittedAt` | string | When the quote was sent (finalised). Read-only. | | `quotes.lastUpdatedAt` | string | When the quote was last updated. Read-only. | | `quotes.createdByUserId` | string | The id of the user who created the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quotes.submittedByUserId` | string | The id of the user who sent the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quotes.tax` | TaxDetail | The tax descriptor for the quote: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `quotes.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `quotes.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `quotes.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `quotes.labels` | Label[] | The labels attached to this quote, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `quotes.labels.labelId` | string | The label's id. | | `quotes.labels.name` | string | Display name of the label. Unique within your company. | | `quotes.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `quotes.labourTotal` | Money | The total charged for labour on the quote: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `quotes.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/quotes **Service:** Quotes **Operation:** `QuoteService_CreateQuote` Submit a complete quote — customer, line items, adjustments, and addresses — in one call, either saved as a draft or finalised and sent. The quote belongs to the company identified by the bearer token. To make a retry safe, send an idempotency key in `requestId`; a repeat with the same key can never create a second quote — it is rejected with HTTP 409 carrying the original quote's id. A customer that can't be resolved or an unknown catalogue id is rejected with HTTP 400 (validation_failure). Quote totals (subtotal, tax amount, total, margin) are derived by the server from the line items, not supplied here. You may send a `tax` descriptor (rate, code, jurisdiction); in this version it is advisory — the server applies your account's configured rate to every taxable line and carries the tax amount actually applied back on the quote (`taxAmount`). A line is exempt when you send `isTaxFree: true` on it, or when you omit `isTaxFree` and the customer has `disableTaxByDefault`; on a kit the components carry that default and the kit line's own flag is read first. See `isTaxFree` on each line kind for the full rule. Every amount is treated as being in your account's currency: amounts are stored as the numbers you send and never converted, so an amount sent in another currency reads back as the same number in your account's currency. ## Request body `CreateQuoteRequest` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `customerId` | string | no | The customer's unique id, as returned by the Customers API. Exactly one of `customerId` or `companyName` must be set; in this version only `customerId` is accepted. | | `companyName` | string | no | The customer's company name. Not yet supported: a request that sets `companyName` instead of `customerId` is rejected with HTTP 501 (not_implemented). Resolve the name with ListCustomers and send `customerId`. | | `lines` | SalesLine[] | no | The quote's line items. Each line is one of the supported line types (on-the-fly, catalogue, labour, notes, flashing, or product kit). | | `lines.onTheFly` | OnTheFlyLine | no | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `lines.onTheFly.productName` | string | yes | Name of the item. Required. | | `lines.onTheFly.productDescription` | string | no | Optional longer description of the item. | | `lines.onTheFly.colour` | string | no | Optional colour for the item. | | `lines.onTheFly.pricing` | LinePricing | no | Pricing for this line. Unit price is required; the line total is required until server-side derivation is enabled for your account (see `totalPrice` on LinePricing), except on custom-formula lines, where it is always required. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.onTheFly.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.onTheFly.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.onTheFly.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.onTheFly.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.onTheFly.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.onTheFly.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.onTheFly.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.onTheFly.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.onTheFly.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.onTheFly.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.onTheFly.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.onTheFly.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.onTheFly.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.onTheFly.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.onTheFly.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.onTheFly.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.onTheFly.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.onTheFly.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.onTheFly.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.onTheFly.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.onTheFly.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. | | `lines.catalogue` | CatalogueLine | no | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `lines.catalogue.productId` | string | yes | The id of the catalogue product for this line. Required, and must match an existing catalogue product; an unknown id rejects the whole request. | | `lines.catalogue.productRowId` | string | no | Optional id of a specific row within the catalogue product. It must be one of the product's rows. The row supplies the line's attributes, cost and unit price when you leave those out. | | `lines.catalogue.rowPriceId` | string | no | Optional id selecting the row's price at one of your price levels — the `rowPriceId` returned by the catalogue (not the price level's own id). It must be a price of `productRowId`; sent without `productRowId`, it must be a price of one of the product's rows, and that row becomes the line's. When you send no `unitPrice`, the line is priced at this level. | | `lines.catalogue.productName` | string | no | Display name for the line. Defaults to the product's name. | | `lines.catalogue.productDescription` | string | no | Longer description for the line. | | `lines.catalogue.categoryName` | string | no | Category name for the line. Defaults to the product's category. | | `lines.catalogue.priceName` | string | no | Price name shown for the line. | | `lines.catalogue.params` | object[] | no | Product parameters as a JSON array of {name, value} entries (for example [{"name": "Colour", "value": "Monument"}]). The colour is taken from the "Colour" parameter and validated against the product's available colours. | | `lines.catalogue.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. Defaults to the row's attributes when the line names a row. | | `lines.catalogue.pricing` | LinePricing | no | Pricing for this line. Parts you leave out come from the catalogue: `pricingStrategy` from the product; `cost` from the row; `unitPrice` from the row's price at `rowPriceId`, or the row's marked-up price (with the row's `markup`) when no price level is chosen. A line that names no row takes only the strategy, and `unitPrice` is yours to send. An omitted `isTaxFree` follows the customer and the product's tax-free setting (see `isTaxFree`). `quantity`, measurements and `totalPrice` follow the rules of every priced line. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.catalogue.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.catalogue.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.catalogue.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.catalogue.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.catalogue.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.catalogue.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.catalogue.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.catalogue.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.catalogue.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.catalogue.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.catalogue.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.catalogue.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.catalogue.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.catalogue.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.catalogue.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.catalogue.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.catalogue.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.catalogue.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.catalogue.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.catalogue.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.labour` | LabourLine | no | A line charging for labour. Set exactly one of the six line-type fields. | | `lines.labour.labourUserId` | string | yes | The id of the worker who performed the labour. Required, and must be an active user in the company that owns the order or quote. | | `lines.labour.productName` | string | no | A short name for the labour. | | `lines.labour.productDescription` | string | no | A longer description of the labour. | | `lines.labour.pricing` | LinePricing | no | Quantity and pricing for this line: the hours in `quantity`, the charge rate in `unitPrice` and the cost rate in `cost`. There are no rate overrides elsewhere on the line, and the worker's own rates are not applied: an omitted `cost` is stored as zero, not taken from the user's `hourlyRateCost`. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.labour.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.labour.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.labour.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.labour.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.labour.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.labour.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.labour.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.labour.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.labour.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.labour.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.labour.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.labour.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.labour.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.labour.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.labour.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.labour.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.labour.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.labour.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.labour.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.labour.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.notes` | NotesLine | no | A note line, with no pricing. Set exactly one of the six line-type fields. | | `lines.notes.notesType` | NotesType | yes | The kind of note. Required. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `lines.notes.notes` | string | no | The note text. | | `lines.notes.productName` | string | no | A short name for the note line. | | `lines.notes.productDescription` | string | no | A longer description for the note line. | | `lines.flashing` | FlashingLine | no | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `lines.flashing.templateId` | string | no | The id of the flashing material or template. Required when the order or quote is submitted. | | `lines.flashing.productName` | string | no | The display name of the flashing material. | | `lines.flashing.priceLevel` | string | no | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `lines.flashing.colour` | string | no | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `lines.flashing.thickness` | string | no | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `lines.flashing.bends` | string | no | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `lines.flashing.totalGirth` | string | no | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `lines.flashing.totalLength` | string | no | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `lines.flashing.unitPrice` | Money | no | The price per metre of the flashing. Required when the order or quote is submitted. | | `lines.flashing.totalPrice` | Money | no | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `lines.flashing.customPrice` | Money | no | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `lines.flashing.customPricePerLength` | Money | no | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `lines.flashing.subitems` | object[] | no | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `lines.flashing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `lines.flashing.drawing` | FlashingDrawing | yes | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `lines.flashing.drawing.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `lines.flashing.drawing.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `lines.flashing.drawing.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `lines.flashing.drawing.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `lines.flashing.drawing.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `lines.flashing.drawing.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `lines.flashing.drawing.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `lines.flashing.drawing.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `lines.flashing.drawing.points.*.id` | string | no | The unique id of this point within the drawing. | | `lines.flashing.drawing.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `lines.flashing.drawing.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `lines.flashing.drawing.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `lines.flashing.drawing.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `lines.flashing.drawing.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `lines.flashing.drawing.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `lines.flashing.drawing.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `lines.flashing.drawing.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `lines.flashing.drawing.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `lines.flashing.drawing.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.drawing.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.drawing.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.drawing.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.drawing.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.drawing.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `lines.flashing.drawing.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `lines.flashing.drawing.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `lines.flashing.drawing.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `lines.flashing.drawing.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.drawing.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.drawing.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.drawing.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.drawing.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.drawing.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `lines.flashing.drawing.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `lines.flashing.drawing.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `lines.flashing.drawing.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `lines.flashing.drawing.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.drawing.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.drawing.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.drawing.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.drawing.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.drawing.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `lines.flashing.drawing.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.drawing.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.drawing.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `lines.flashing.drawing.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `lines.flashing.drawing.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `lines.flashing.drawing.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `lines.flashing.drawing.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `lines.flashing.drawing.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `lines.flashing.drawing.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `lines.flashing.drawing.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `lines.flashing.drawing.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `lines.flashing.drawing.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.drawing.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.drawing.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `lines.flashing.drawing.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `lines.flashing.drawing.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.drawing.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.drawing.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `lines.flashing.otherSides` | FlashingDrawing[] | no | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `lines.flashing.otherSides.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `lines.flashing.otherSides.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `lines.flashing.otherSides.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `lines.flashing.otherSides.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `lines.flashing.otherSides.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `lines.flashing.otherSides.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `lines.flashing.otherSides.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `lines.flashing.otherSides.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `lines.flashing.otherSides.points.*.id` | string | no | The unique id of this point within the drawing. | | `lines.flashing.otherSides.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `lines.flashing.otherSides.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `lines.flashing.otherSides.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `lines.flashing.otherSides.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `lines.flashing.otherSides.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `lines.flashing.otherSides.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `lines.flashing.otherSides.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `lines.flashing.otherSides.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `lines.flashing.otherSides.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `lines.flashing.otherSides.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.otherSides.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.otherSides.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.otherSides.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `lines.flashing.otherSides.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `lines.flashing.otherSides.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `lines.flashing.otherSides.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `lines.flashing.otherSides.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.otherSides.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.otherSides.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.otherSides.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `lines.flashing.otherSides.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `lines.flashing.otherSides.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `lines.flashing.otherSides.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `lines.flashing.otherSides.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `lines.flashing.otherSides.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `lines.flashing.otherSides.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `lines.flashing.otherSides.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `lines.flashing.otherSides.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `lines.flashing.otherSides.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `lines.flashing.otherSides.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `lines.flashing.otherSides.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `lines.flashing.otherSides.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `lines.flashing.otherSides.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `lines.flashing.otherSides.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `lines.flashing.otherSides.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `lines.flashing.otherSides.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `lines.flashing.otherSides.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `lines.flashing.otherSides.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `lines.flashing.otherSides.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `lines.flashing.otherSides.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `lines.flashing.otherSides.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `lines.flashing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.productKit` | ProductKitLine | no | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `lines.productKit.kitId` | string | no | The kit's id (`kitId` on Kit, from the catalogue read surface). Optional: omit it to define an on-the-fly kit not linked to any catalogue kit — the kit is described entirely by this line's name, pricing, components, and sub-kits. | | `lines.productKit.kitRowId` | string | no | The selected kit row/variant id. Optional. | | `lines.productKit.productName` | string | no | Display name of the kit. | | `lines.productKit.customPricing` | boolean | no | Whether the kit is custom-priced (true) rather than computed from its components (false). | | `lines.productKit.description` | string | no | Free-text description of the kit. | | `lines.productKit.displayKitItems` | object[] | no | Which documents and views show the kit's components, as a JSON array of display-target keys. The keys are "displayOnAllViews", "displayOnWorkOrderPdf", "displayOnDeliveryDocketPdf", "displayOnInvoicePdf", "displayOnQuotePdf", "displayOnProformaInvoicePdf", "displayOnCustomerOnlineView" and "displayOnAccountingPackage"; a key that is not one of these matches no document. Optional: when omitted, the kit takes your company's "Display individual Product Kit items on" setting, as a kit built in the Factory app does. An empty array shows the components on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `lines.productKit.displaySubKits` | object[] | no | Which documents and views show the kit's sub-kits, as a JSON array of the same display-target keys as `displayKitItems`. Optional: when omitted, the kit takes your company's "Display Product Kit sub-kits on" setting, as a kit built in the Factory app does. An empty array shows the sub-kits on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `lines.productKit.pricing` | LinePricing | no | Overall pricing for the kit line. Its `isTaxFree` is read before the components': a tax-free kit line exempts the whole kit whatever the components say, a taxable one lets each component's own `isTaxFree` decide, and a custom-priced kit is taxed on the kit line alone. Omit it and it depends on the kit: on a standard kit it is false, with the components following the customer, so a component you mark taxable is charged even for an exempt customer; on a custom-priced kit it follows the customer, so an exempt customer's custom kit is exempt in full. A value you send, true or false, is kept as sent. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `lines.productKit.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `lines.productKit.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `lines.productKit.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `lines.productKit.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `lines.productKit.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `lines.productKit.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `lines.productKit.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `lines.productKit.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `lines.productKit.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.productKit.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `lines.productKit.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `lines.productKit.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `lines.productKit.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.attributes` | object[] | no | Additional named attributes for the kit line, as a JSON array of {name, value} entries; for measurement-priced kits they describe the measurement entries. | | `lines.productKit.components` | KitComponent[] | no | The kit's direct components. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `lines.productKit.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `lines.productKit.components.productId` | string | no | The catalogue product id for this component. Optional. | | `lines.productKit.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `lines.productKit.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `lines.productKit.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `lines.productKit.components.productDescription` | string | no | Free-text description of the component. | | `lines.productKit.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `lines.productKit.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `lines.productKit.components.unitPrice` | Money | no | Price per unit. | | `lines.productKit.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `lines.productKit.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `lines.productKit.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `lines.productKit.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `lines.productKit.components.cost` | Money | no | Cost of the component. | | `lines.productKit.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `lines.productKit.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `lines.productKit.components.margin` | Money | no | Margin earned on the component. | | `lines.productKit.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.productKit.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `lines.productKit.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `lines.productKit.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `lines.productKit.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `lines.productKit.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `lines.productKit.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `lines.productKit.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `lines.productKit.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `lines.productKit.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `lines.productKit.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.subKits` | SubKit[] | no | The kit's sub-assemblies, each with its own components. A sub-assembly within a kit. A sub-kit groups a set of components under its own title and quantity, letting a kit be organized into named sub-assemblies. | | `lines.productKit.subKits.title` | string | no | Display title of the sub-assembly. | | `lines.productKit.subKits.quantity` | string | no | Quantity of this sub-assembly, as a decimal string. | | `lines.productKit.subKits.actualQuantity` | string | no | Actual measured quantity, as a decimal string. | | `lines.productKit.subKits.unitPrice` | Money | no | Price per unit of the sub-assembly. | | `lines.productKit.subKits.totalPrice` | Money | no | Total price of the sub-assembly. | | `lines.productKit.subKits.components` | KitComponent[] | no | The components that make up this sub-assembly. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `lines.productKit.subKits.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `lines.productKit.subKits.components.productId` | string | no | The catalogue product id for this component. Optional. | | `lines.productKit.subKits.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `lines.productKit.subKits.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `lines.productKit.subKits.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `lines.productKit.subKits.components.productDescription` | string | no | Free-text description of the component. | | `lines.productKit.subKits.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `lines.productKit.subKits.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `lines.productKit.subKits.components.unitPrice` | Money | no | Price per unit. | | `lines.productKit.subKits.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `lines.productKit.subKits.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `lines.productKit.subKits.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `lines.productKit.subKits.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `lines.productKit.subKits.components.cost` | Money | no | Cost of the component. | | `lines.productKit.subKits.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `lines.productKit.subKits.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `lines.productKit.subKits.components.margin` | Money | no | Margin earned on the component. | | `lines.productKit.subKits.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `lines.productKit.subKits.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `lines.productKit.subKits.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `lines.productKit.subKits.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `lines.productKit.subKits.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `lines.productKit.subKits.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `lines.productKit.subKits.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `lines.productKit.subKits.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `lines.productKit.subKits.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `lines.productKit.subKits.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `lines.productKit.subKits.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.subKits.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.subKits.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.subKits.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.productKit.subKits.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `lines.productKit.subKits.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `lines.productKit.subKits.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `lines.productKit.subKits.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `lines.id` | string | no | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `adjustments` | Adjustment[] | no | Quote-level fees, discounts, and markups applied across the whole quote. | | `adjustments.type` | AdjustmentType | yes | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `adjustments.title` | string | no | A label describing the adjustment. | | `adjustments.amount` | Money | no | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `adjustments.amount.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `adjustments.amount.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `adjustments.percent` | string | no | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `isSubmitted` | boolean | no | Finalise and send the quote immediately (true) or save it as a draft (false). | | `fulfilmentMethod` | FulfilmentMethod | no | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. Determines which address below applies. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `billingAddress` | Address | no | Billing address for the quote. | | `billingAddress.address1` | string | no | First address line (street number and name). | | `billingAddress.address2` | string | no | Second address line (unit, suite, or similar). | | `billingAddress.city` | string | no | City or suburb. | | `billingAddress.state` | string | no | State, province, or region. | | `billingAddress.postalCode` | string | no | Postal code (ZIP code, postcode). | | `billingAddress.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `deliveryAddress` | Address | no | Delivery address. Used only when `fulfilmentMethod` is DELIVERY. | | `deliveryAddress.address1` | string | no | First address line (street number and name). | | `deliveryAddress.address2` | string | no | Second address line (unit, suite, or similar). | | `deliveryAddress.city` | string | no | City or suburb. | | `deliveryAddress.state` | string | no | State, province, or region. | | `deliveryAddress.postalCode` | string | no | Postal code (ZIP code, postcode). | | `deliveryAddress.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `installAddress` | Address | no | Installation address. Used only when `fulfilmentMethod` is INSTALL. | | `installAddress.address1` | string | no | First address line (street number and name). | | `installAddress.address2` | string | no | Second address line (unit, suite, or similar). | | `installAddress.city` | string | no | City or suburb. | | `installAddress.state` | string | no | State, province, or region. | | `installAddress.postalCode` | string | no | Postal code (ZIP code, postcode). | | `installAddress.countryCode` | string | no | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `reference` | string | no | Free-text external reference for this quote — e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. Stored for lookup and audit; distinct from `requestId` (the retry key) and from the read-only `orderNumber` (Factory's own number for the quote). | | `customFields` | object | no | Values for your account's custom-defined sales-document fields, keyed by each field's configured key. Unknown keys, keys of other modules, and values that don't match the field's type are rejected. Send number and currency values as numbers or decimal strings, checkboxes as booleans, multi-selects as arrays of strings, and everything else as strings. Select and multi-select values must be option keys from the field's definition, never display labels; discover your account's field keys and options with GET /v1/company/custom-fields. Formula fields are computed and cannot be written. | | `requiredAt` | string | no | The date the customer needs the order by, if the quote is accepted. | | `contact` | Contact | no | The point-of-contact person for this quote, distinct from the customer. | | `contact.name` | string | no | The contact person's name. | | `contact.email` | string | no | The contact's email address. Optional; when provided, it must be a valid email address. | | `contact.phone` | string | no | The contact's landline phone number. | | `contact.mobile` | string | no | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `notes` | string | no | Free-text notes about the quote. | | `quotedAt` | string | no | The date the quote was issued. If omitted, the server sets it. | | `tax` | TaxDetail | no | The tax to apply, as a rate/code/jurisdiction descriptor. Optional and advisory in this version: the server applies your account's configured tax settings and echoes the values actually applied (`tax` and `taxAmount` on the Quote) — compare them to detect an override. | | `tax.rate` | string | no | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `tax.code` | string | no | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `tax.jurisdiction` | string | no | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `labelIds` | string[] | no | Labels to attach to the quote as part of creating it, by id. Every id must exist in your company's label set (discover them with GET /v1/company/labels): an unknown id fails the whole call with HTTP 404 before the quote is created. Labelling happens with the create but not atomically inside it — in the rare case the quote is created and labelling then fails, the error names the created quote id and the quote exists WITHOUT labels; attach them with SetQuoteLabels. The created quote echoes the attached labels. Duplicate ids are rejected. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `CreateQuoteResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the quote it created), its outcome is still unknown, or the key was reused with a materially different body. Verify the existing quote with GetQuote or ListQuotes, then send a new `requestId` to create a new quote. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `CreateQuoteResponse` | Field | Type | Description | | --- | --- | --- | | `quote` | Quote | The created quote, including server-set fields and assigned identifiers. | | `quote.quoteId` | string | The quote's unique id. Read-only. | | `quote.isSubmitted` | boolean | Whether the quote has been finalised and sent (true) or is still a draft (false). | | `quote.customerId` | string | The id of the customer this quote belongs to. Always present. Read-only. | | `quote.customerCompanyName` | string | The customer's company name. Read-only. | | `quote.subtotal` | Money | Quote subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `quote.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.taxAmount` | Money | Total tax for the quote, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `quote.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.total` | Money | Quote grand total. Derived by the server; read-only. | | `quote.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.margin` | Money | Quote margin, computed by the server from the line items. Read-only. | | `quote.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.totalCost` | Money | Total cost of the quote, computed by the server from the line items. Read-only. | | `quote.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.discountAmount` | Money | The quote-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `quote.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.lines` | SalesLine[] | The quote's line items. | | `quote.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `quote.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `quote.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `quote.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `quote.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `quote.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `quote.lines.flashing.productName` | string | The display name of the flashing material. | | `quote.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `quote.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `quote.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `quote.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `quote.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `quote.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `quote.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `quote.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `quote.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `quote.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `quote.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `quote.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `quote.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `quote.adjustments` | Adjustment[] | Quote-level fees, discounts, and markups. At most one fee per quote is allowed. | | `quote.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `quote.adjustments.title` | string | A label describing the adjustment. | | `quote.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `quote.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `quote.fulfilmentMethod` | FulfilmentMethod | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `quote.billingAddress` | Address | Billing address for the quote. | | `quote.billingAddress.address1` | string | First address line (street number and name). | | `quote.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.billingAddress.city` | string | City or suburb. | | `quote.billingAddress.state` | string | State, province, or region. | | `quote.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `quote.deliveryAddress.address1` | string | First address line (street number and name). | | `quote.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.deliveryAddress.city` | string | City or suburb. | | `quote.deliveryAddress.state` | string | State, province, or region. | | `quote.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `quote.installAddress.address1` | string | First address line (street number and name). | | `quote.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.installAddress.city` | string | City or suburb. | | `quote.installAddress.state` | string | State, province, or region. | | `quote.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryFee` | Money | The delivery fee quoted on the order. Read-only. | | `quote.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.reference` | string | Free-text external reference, e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. | | `quote.orderNumber` | string | Factory's number for this quote. Assigned by the server when the quote is created, sequential within your company, and never changed — the quote keeps this number if it becomes an order. Shown as "Quote #" on the quote PDF and email, as "Order #" in the Factory app (order list, order page, workflow board) and on invoices and delivery documents, and used as the document reference in the accounting integrations. Read-only. | | `quote.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `quote.contact` | Contact | The quote's point-of-contact person. | | `quote.contact.name` | string | The contact person's name. | | `quote.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `quote.contact.phone` | string | The contact's landline phone number. | | `quote.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `quote.notes` | string | Free-text notes about the quote. | | `quote.requiredAt` | string | The date the customer needs the order by, if the quote is accepted. | | `quote.quotedAt` | string | The date the quote was issued. | | `quote.createdAt` | string | When the quote was created. Read-only. | | `quote.submittedAt` | string | When the quote was sent (finalised). Read-only. | | `quote.lastUpdatedAt` | string | When the quote was last updated. Read-only. | | `quote.createdByUserId` | string | The id of the user who created the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.submittedByUserId` | string | The id of the user who sent the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.tax` | TaxDetail | The tax descriptor for the quote: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `quote.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `quote.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `quote.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `quote.labels` | Label[] | The labels attached to this quote, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `quote.labels.labelId` | string | The label's id. | | `quote.labels.name` | string | Display name of the label. Unique within your company. | | `quote.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `quote.labourTotal` | Money | The total charged for labour on the quote: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `quote.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `drawings` | DrawingIdMapping[] | The server identities of the flashing drawings created by this call, one entry per flashing line in the order you sent them, each pairing your `tempId` with the assigned `drawingId` — use them to address UploadDrawingSvg. Only flashing lines carry drawings; empty when the quote had no flashing lines. | | `drawings.tempId` | string | The `tempId` you supplied on the drawing, echoed verbatim; empty when you did not supply one (position in the list still identifies the drawing). | | `drawings.drawingId` | string | The server-assigned id of the drawing. | | `drawings.otherSides` | DrawingIdMapping[] | The identities of the drawing's other sides, in the order you sent them. | | `drawings.otherSides.tempId` | string | The `tempId` you supplied on the drawing, echoed verbatim; empty when you did not supply one (position in the list still identifies the drawing). | | `drawings.otherSides.drawingId` | string | The server-assigned id of the drawing. | | `drawings.otherSides.otherSides` | DrawingIdMapping[] | The identities of the drawing's other sides, in the order you sent them. | --- # GET /v1/quotes/{quoteId} **Service:** Quotes **Operation:** `QuoteService_GetQuote` Returns the full quote — header, lines, adjustments, totals, and server-derived fields — for the given id, scoped to the authenticated company. Resolves quotes only: once a quote has been accepted and converted to an order in the Factory app, its id returns HTTP 404 (not_found) here and the document is read with GetOrder instead. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetQuoteResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, or the quote has been accepted and converted to an order — read it with GetOrder, swapping the id's quote_ prefix for order_ (the 26-character suffix stays the same). | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetQuoteResponse` | Field | Type | Description | | --- | --- | --- | | `quote` | Quote | The requested quote. | | `quote.quoteId` | string | The quote's unique id. Read-only. | | `quote.isSubmitted` | boolean | Whether the quote has been finalised and sent (true) or is still a draft (false). | | `quote.customerId` | string | The id of the customer this quote belongs to. Always present. Read-only. | | `quote.customerCompanyName` | string | The customer's company name. Read-only. | | `quote.subtotal` | Money | Quote subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `quote.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.taxAmount` | Money | Total tax for the quote, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `quote.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.total` | Money | Quote grand total. Derived by the server; read-only. | | `quote.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.margin` | Money | Quote margin, computed by the server from the line items. Read-only. | | `quote.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.totalCost` | Money | Total cost of the quote, computed by the server from the line items. Read-only. | | `quote.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.discountAmount` | Money | The quote-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `quote.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.lines` | SalesLine[] | The quote's line items. | | `quote.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `quote.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `quote.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `quote.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `quote.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `quote.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `quote.lines.flashing.productName` | string | The display name of the flashing material. | | `quote.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `quote.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `quote.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `quote.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `quote.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `quote.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `quote.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `quote.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `quote.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `quote.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `quote.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `quote.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `quote.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `quote.adjustments` | Adjustment[] | Quote-level fees, discounts, and markups. At most one fee per quote is allowed. | | `quote.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `quote.adjustments.title` | string | A label describing the adjustment. | | `quote.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `quote.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `quote.fulfilmentMethod` | FulfilmentMethod | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `quote.billingAddress` | Address | Billing address for the quote. | | `quote.billingAddress.address1` | string | First address line (street number and name). | | `quote.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.billingAddress.city` | string | City or suburb. | | `quote.billingAddress.state` | string | State, province, or region. | | `quote.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `quote.deliveryAddress.address1` | string | First address line (street number and name). | | `quote.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.deliveryAddress.city` | string | City or suburb. | | `quote.deliveryAddress.state` | string | State, province, or region. | | `quote.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `quote.installAddress.address1` | string | First address line (street number and name). | | `quote.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.installAddress.city` | string | City or suburb. | | `quote.installAddress.state` | string | State, province, or region. | | `quote.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryFee` | Money | The delivery fee quoted on the order. Read-only. | | `quote.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.reference` | string | Free-text external reference, e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. | | `quote.orderNumber` | string | Factory's number for this quote. Assigned by the server when the quote is created, sequential within your company, and never changed — the quote keeps this number if it becomes an order. Shown as "Quote #" on the quote PDF and email, as "Order #" in the Factory app (order list, order page, workflow board) and on invoices and delivery documents, and used as the document reference in the accounting integrations. Read-only. | | `quote.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `quote.contact` | Contact | The quote's point-of-contact person. | | `quote.contact.name` | string | The contact person's name. | | `quote.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `quote.contact.phone` | string | The contact's landline phone number. | | `quote.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `quote.notes` | string | Free-text notes about the quote. | | `quote.requiredAt` | string | The date the customer needs the order by, if the quote is accepted. | | `quote.quotedAt` | string | The date the quote was issued. | | `quote.createdAt` | string | When the quote was created. Read-only. | | `quote.submittedAt` | string | When the quote was sent (finalised). Read-only. | | `quote.lastUpdatedAt` | string | When the quote was last updated. Read-only. | | `quote.createdByUserId` | string | The id of the user who created the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.submittedByUserId` | string | The id of the user who sent the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.tax` | TaxDetail | The tax descriptor for the quote: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `quote.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `quote.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `quote.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `quote.labels` | Label[] | The labels attached to this quote, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `quote.labels.labelId` | string | The label's id. | | `quote.labels.name` | string | Display name of the label. Unique within your company. | | `quote.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `quote.labourTotal` | Money | The total charged for labour on the quote: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `quote.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # GET /v1/quotes/{quoteId}/attachments **Service:** Quotes **Operation:** `QuoteService_ListAttachments` Returns every file on the quote's conversation as a flat list, oldest first: files your team attached and documents the Factory app generated (`isGenerated` distinguishes them). Each attachment carries a presigned download URL. Signature captures collected in the app are not returned. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote whose attachments to list. Required. | | `pageSize` | query | integer | no | Maximum number of attachments to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `quotesListAttachmentsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, or the quote has been accepted and converted to an order — its conversation carries over: read it with the order-side endpoint, swapping the id's quote_ prefix for order_ (the 26-character suffix stays the same). | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `quotesListAttachmentsResponse` | Field | Type | Description | | --- | --- | --- | | `attachments` | Attachment[] | The attachments in this page, oldest first. | | `attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `attachments.filename` | string | The file's name, as uploaded. | | `attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/quotes/{quoteId}/attachments **Service:** Quotes **Operation:** `QuoteService_UploadAttachment` Uploads one file, up to 20 MB, into the quote's conversation, where it appears immediately in the Factory app's Collaborate tab. Send the file content (base64-encoded over REST) with its filename and, optionally, a plain-text caption — with a caption the upload reads as a message carrying a file; without one it is a bare attachment, which is how most files are posted. The created conversation message is returned, including the attachment's id and a presigned download URL. HEIC and HEIF images are converted to JPEG on upload. To make a retry safe, send an idempotency key in `requestId`; a repeat with the same key can never attach the file twice — it is rejected with HTTP 409 carrying the original message's id. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote to attach to. Required. | ## Request body `QuoteServiceUploadAttachmentBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `file` | string | yes | The file content, up to 20 MB. Required. | | `filename` | string | yes | The file's name, including its extension. Required. | | `contentType` | string | no | The file's media type (for example `image/png`). Optional; derived from the filename when omitted. | | `caption` | string | no | Optional plain-text caption. The upload always creates one conversation message carrying the file; the caption becomes that message's text, stored in the same HTML-escaped, `

`-wrapped form as a posted message. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `quotesUploadAttachmentResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation — the body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). This includes a file over the 20 MB limit and a HEIC image that could not be converted. | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, or the quote has been accepted and converted to an order — its conversation carries over: attach via the order-side endpoint, swapping the id's quote_ prefix for order_ (the 26-character suffix stays the same). | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the message it created), its outcome is still unknown, or the key was reused with a materially different body. Verify the conversation with ListMessages or ListAttachments, then send a new `requestId` to attach a new file. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `quotesUploadAttachmentResponse` | Field | Type | Description | | --- | --- | --- | | `message` | Message | The created message, carrying the uploaded file as its single attachment (with its id and a presigned download URL). | | `message.id` | string | The id of this message. | | `message.authorName` | string | The display name of the person who wrote the message. Empty when the author's account no longer exists. | | `message.authorEmail` | string | The email address of the person who wrote the message. Empty when the author's account no longer exists. | | `message.text` | string | The message text, as an HTML fragment. Messages written in the Factory app carry the app's markup (including @-mentions); plain text posted through this API is HTML-escaped and `

`-wrapped on write, and reads back in that stored form. Empty for file-only messages — most attachments are posted without any text. | | `message.createdAt` | string | When the message was posted. | | `message.attachments` | Attachment[] | The files attached to this message, if any. | | `message.attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `message.attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `message.attachments.filename` | string | The file's name, as uploaded. | | `message.attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `message.attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `message.attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `message.attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `message.attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `message.attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | --- # GET /v1/quotes/{quoteId}/attachments/{attachmentId} **Service:** Quotes **Operation:** `QuoteService_GetAttachment` Returns the attachment's details and a fresh presigned download URL. Use this when a stored URL from an earlier read has expired. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote the attachment belongs to. Required. | | `attachmentId` | path | string | yes | The id of the attachment to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `quotesGetAttachmentResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, the attachment is not part of this quote's conversation, or it has been removed. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `quotesGetAttachmentResponse` | Field | Type | Description | | --- | --- | --- | | `attachment` | Attachment | The requested attachment, with a fresh presigned download URL. | | `attachment.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `attachment.messageId` | string | The id of the conversation message this attachment belongs to. | | `attachment.filename` | string | The file's name, as uploaded. | | `attachment.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `attachment.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `attachment.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `attachment.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `attachment.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `attachment.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | --- # DELETE /v1/quotes/{quoteId}/attachments/{attachmentId} **Service:** Quotes **Operation:** `QuoteService_DeleteAttachment` Removes the file from the quote's conversation and from attachment lists. The removal is reversible by a Factory app administrator. Only the person who posted the file or an administrator can remove it; for an API key, the acting user is the person the key belongs to, so a key can always remove files it uploaded. Documents generated by the Factory app (`isGenerated`) cannot be removed. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote the attachment belongs to. Required. | | `attachmentId` | path | string | yes | The id of the attachment to remove. Required. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `quotesDeleteAttachmentResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation, or the attachment is a document generated by the Factory app, which cannot be removed (failed_precondition). The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The acting user did not post this file and is not an administrator. | | 404 | `Error` | No quote with the given id exists for the authenticated company, the attachment is not part of this quote's conversation, or it has already been removed. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | --- # POST /v1/quotes/{quoteId}/drawings:uploadSvg **Service:** Quotes **Operation:** `QuoteService_UploadDrawingSvg` Drawings exist only on flashing lines: a drawing is the folded profile of a sheet-metal flashing, not a general image or file attachment for the quote. A quote with no flashing lines has no drawings, and this is the only surface that accepts an upload. Call this after creating a quote: map each drawing's `tempId` to the `drawingId` returned on the created quote, then upload the rendered SVG for each `drawingId`. Stored SVGs are returned as a presigned URL (`svgUrl` on the FlashingDrawing) on later reads. Each drawing's outcome is reported individually; a drawing is skipped if it isn't part of this quote, isn't an image/svg+xml, or has been deleted. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote whose flashing drawings are being uploaded. Required. | ## Request body `QuoteServiceUploadDrawingSvgBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `svgs` | DrawingSvg[] | yes | The flashing drawings and their rendered SVGs. At least one is required. | | `svgs.drawingId` | string | yes | The id of the flashing drawing this SVG belongs to. Required. | | `svgs.svg` | string | yes | The rendered SVG image content, sent base64-encoded in JSON. The decoded image must be at most 2,000,000 bytes (the base64 text is about a third larger). Required. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `quotesUploadDrawingSvgResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `quotesUploadDrawingSvgResponse` | Field | Type | Description | | --- | --- | --- | | `results` | DrawingSvgResult[] | One result per submitted flashing drawing, in request order. | | `results.drawingId` | string | The drawing this result refers to. | | `results.uploaded` | boolean | Whether the SVG was stored successfully. | | `results.svgUrl` | string | On success, a presigned URL to the stored SVG. | | `results.error` | string | On failure, the reason: the drawing isn't part of this order or quote, isn't an image/svg+xml, or has been deleted. | --- # PUT /v1/quotes/{quoteId}/labels **Service:** Quotes **Operation:** `QuoteService_SetQuoteLabels` The list you send becomes the quote's complete label set: labels not listed are removed, and an empty list removes them all. Look up label ids with ListLabels. A label id that does not exist in your company is rejected with HTTP 404 (not_found) and nothing is changed. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote, as returned by CreateQuote. | ## Request body `SetQuoteLabelsBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `labelIds` | string[] | no | The quote's desired complete label set. An empty list removes every label. Duplicate ids are rejected. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `SetQuoteLabelsResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation — for example a badly formed or duplicated label id. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, or the quote has been accepted and converted to an order — label it with SetOrderLabels, swapping the id's quote_ prefix for order_ (the 26-character suffix stays the same). Also returned when a label id does not exist in your company: nothing is changed and the quote's labels are left as they were. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `SetQuoteLabelsResponse` | Field | Type | Description | | --- | --- | --- | | `labels` | Label[] | The quote's labels after the change, oldest attachment first. | | `labels.labelId` | string | The label's id. | | `labels.name` | string | Display name of the label. Unique within your company. | | `labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | --- # POST /v1/quotes/{quoteId}/lines **Service:** Quotes **Operation:** `QuoteService_AddQuoteLineItem` Build a quote up over time: each call creates one line under the quote and recomputes the quote's totals. One line per call; add several lines with several calls. The new line's position among the existing lines is not controllable and is not guaranteed to be last; read the quote for its final order. Adding a line does not change whether the quote is submitted. Pair with CreateQuote, which imports a whole quote in one atomic call (or creates an empty quote you then add lines to with this). ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote to add the line item to, as returned by CreateQuote. | ## Request body `AddQuoteLineItemBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `line` | SalesLine | yes | The line item to add: one of the supported line types (on-the-fly, catalogue, labour, notes, or product kit). | | `line.onTheFly` | OnTheFlyLine | no | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `line.onTheFly.productName` | string | yes | Name of the item. Required. | | `line.onTheFly.productDescription` | string | no | Optional longer description of the item. | | `line.onTheFly.colour` | string | no | Optional colour for the item. | | `line.onTheFly.pricing` | LinePricing | no | Pricing for this line. Unit price is required; the line total is required until server-side derivation is enabled for your account (see `totalPrice` on LinePricing), except on custom-formula lines, where it is always required. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.onTheFly.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.onTheFly.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.onTheFly.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.onTheFly.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.onTheFly.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.onTheFly.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.onTheFly.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.onTheFly.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.onTheFly.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.onTheFly.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.onTheFly.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.onTheFly.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.onTheFly.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.onTheFly.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.onTheFly.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.onTheFly.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.onTheFly.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.onTheFly.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.onTheFly.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.onTheFly.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.onTheFly.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. | | `line.catalogue` | CatalogueLine | no | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `line.catalogue.productId` | string | yes | The id of the catalogue product for this line. Required, and must match an existing catalogue product; an unknown id rejects the whole request. | | `line.catalogue.productRowId` | string | no | Optional id of a specific row within the catalogue product. It must be one of the product's rows. The row supplies the line's attributes, cost and unit price when you leave those out. | | `line.catalogue.rowPriceId` | string | no | Optional id selecting the row's price at one of your price levels — the `rowPriceId` returned by the catalogue (not the price level's own id). It must be a price of `productRowId`; sent without `productRowId`, it must be a price of one of the product's rows, and that row becomes the line's. When you send no `unitPrice`, the line is priced at this level. | | `line.catalogue.productName` | string | no | Display name for the line. Defaults to the product's name. | | `line.catalogue.productDescription` | string | no | Longer description for the line. | | `line.catalogue.categoryName` | string | no | Category name for the line. Defaults to the product's category. | | `line.catalogue.priceName` | string | no | Price name shown for the line. | | `line.catalogue.params` | object[] | no | Product parameters as a JSON array of {name, value} entries (for example [{"name": "Colour", "value": "Monument"}]). The colour is taken from the "Colour" parameter and validated against the product's available colours. | | `line.catalogue.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. Defaults to the row's attributes when the line names a row. | | `line.catalogue.pricing` | LinePricing | no | Pricing for this line. Parts you leave out come from the catalogue: `pricingStrategy` from the product; `cost` from the row; `unitPrice` from the row's price at `rowPriceId`, or the row's marked-up price (with the row's `markup`) when no price level is chosen. A line that names no row takes only the strategy, and `unitPrice` is yours to send. An omitted `isTaxFree` follows the customer and the product's tax-free setting (see `isTaxFree`). `quantity`, measurements and `totalPrice` follow the rules of every priced line. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.catalogue.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.catalogue.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.catalogue.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.catalogue.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.catalogue.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.catalogue.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.catalogue.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.catalogue.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.catalogue.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.catalogue.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.catalogue.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.catalogue.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.catalogue.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.catalogue.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.catalogue.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.catalogue.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.catalogue.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.catalogue.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.catalogue.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.catalogue.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.labour` | LabourLine | no | A line charging for labour. Set exactly one of the six line-type fields. | | `line.labour.labourUserId` | string | yes | The id of the worker who performed the labour. Required, and must be an active user in the company that owns the order or quote. | | `line.labour.productName` | string | no | A short name for the labour. | | `line.labour.productDescription` | string | no | A longer description of the labour. | | `line.labour.pricing` | LinePricing | no | Quantity and pricing for this line: the hours in `quantity`, the charge rate in `unitPrice` and the cost rate in `cost`. There are no rate overrides elsewhere on the line, and the worker's own rates are not applied: an omitted `cost` is stored as zero, not taken from the user's `hourlyRateCost`. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.labour.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.labour.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.labour.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.labour.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.labour.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.labour.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.labour.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.labour.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.labour.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.labour.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.labour.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.labour.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.labour.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.labour.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.labour.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.labour.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.labour.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.labour.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.labour.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.labour.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.notes` | NotesLine | no | A note line, with no pricing. Set exactly one of the six line-type fields. | | `line.notes.notesType` | NotesType | yes | The kind of note. Required. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `line.notes.notes` | string | no | The note text. | | `line.notes.productName` | string | no | A short name for the note line. | | `line.notes.productDescription` | string | no | A longer description for the note line. | | `line.flashing` | FlashingLine | no | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `line.flashing.templateId` | string | no | The id of the flashing material or template. Required when the order or quote is submitted. | | `line.flashing.productName` | string | no | The display name of the flashing material. | | `line.flashing.priceLevel` | string | no | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `line.flashing.colour` | string | no | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `line.flashing.thickness` | string | no | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `line.flashing.bends` | string | no | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `line.flashing.totalGirth` | string | no | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `line.flashing.totalLength` | string | no | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `line.flashing.unitPrice` | Money | no | The price per metre of the flashing. Required when the order or quote is submitted. | | `line.flashing.totalPrice` | Money | no | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `line.flashing.customPrice` | Money | no | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `line.flashing.customPricePerLength` | Money | no | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `line.flashing.subitems` | object[] | no | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `line.flashing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `line.flashing.drawing` | FlashingDrawing | yes | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `line.flashing.drawing.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `line.flashing.drawing.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `line.flashing.drawing.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `line.flashing.drawing.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `line.flashing.drawing.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `line.flashing.drawing.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `line.flashing.drawing.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `line.flashing.drawing.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `line.flashing.drawing.points.*.id` | string | no | The unique id of this point within the drawing. | | `line.flashing.drawing.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `line.flashing.drawing.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `line.flashing.drawing.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `line.flashing.drawing.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `line.flashing.drawing.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `line.flashing.drawing.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `line.flashing.drawing.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `line.flashing.drawing.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `line.flashing.drawing.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `line.flashing.drawing.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.drawing.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.drawing.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.drawing.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.drawing.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.drawing.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `line.flashing.drawing.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `line.flashing.drawing.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `line.flashing.drawing.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `line.flashing.drawing.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.drawing.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.drawing.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.drawing.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.drawing.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.drawing.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `line.flashing.drawing.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `line.flashing.drawing.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `line.flashing.drawing.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `line.flashing.drawing.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.drawing.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.drawing.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.drawing.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.drawing.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.drawing.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `line.flashing.drawing.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.drawing.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.drawing.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `line.flashing.drawing.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `line.flashing.drawing.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `line.flashing.drawing.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `line.flashing.drawing.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `line.flashing.drawing.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `line.flashing.drawing.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `line.flashing.drawing.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `line.flashing.drawing.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `line.flashing.drawing.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.drawing.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.drawing.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `line.flashing.drawing.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `line.flashing.drawing.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.drawing.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.drawing.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `line.flashing.otherSides` | FlashingDrawing[] | no | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `line.flashing.otherSides.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `line.flashing.otherSides.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `line.flashing.otherSides.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `line.flashing.otherSides.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `line.flashing.otherSides.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `line.flashing.otherSides.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `line.flashing.otherSides.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `line.flashing.otherSides.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `line.flashing.otherSides.points.*.id` | string | no | The unique id of this point within the drawing. | | `line.flashing.otherSides.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `line.flashing.otherSides.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `line.flashing.otherSides.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `line.flashing.otherSides.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `line.flashing.otherSides.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `line.flashing.otherSides.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `line.flashing.otherSides.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `line.flashing.otherSides.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `line.flashing.otherSides.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `line.flashing.otherSides.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.otherSides.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.otherSides.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.otherSides.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.otherSides.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.otherSides.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `line.flashing.otherSides.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `line.flashing.otherSides.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `line.flashing.otherSides.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `line.flashing.otherSides.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.otherSides.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.otherSides.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.otherSides.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.otherSides.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.otherSides.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `line.flashing.otherSides.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `line.flashing.otherSides.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `line.flashing.otherSides.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `line.flashing.otherSides.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `line.flashing.otherSides.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `line.flashing.otherSides.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `line.flashing.otherSides.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `line.flashing.otherSides.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `line.flashing.otherSides.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `line.flashing.otherSides.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.otherSides.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `line.flashing.otherSides.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `line.flashing.otherSides.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `line.flashing.otherSides.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `line.flashing.otherSides.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `line.flashing.otherSides.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `line.flashing.otherSides.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `line.flashing.otherSides.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `line.flashing.otherSides.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `line.flashing.otherSides.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `line.flashing.otherSides.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.otherSides.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `line.flashing.otherSides.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `line.flashing.otherSides.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `line.flashing.otherSides.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.otherSides.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `line.flashing.otherSides.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `line.flashing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.productKit` | ProductKitLine | no | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `line.productKit.kitId` | string | no | The kit's id (`kitId` on Kit, from the catalogue read surface). Optional: omit it to define an on-the-fly kit not linked to any catalogue kit — the kit is described entirely by this line's name, pricing, components, and sub-kits. | | `line.productKit.kitRowId` | string | no | The selected kit row/variant id. Optional. | | `line.productKit.productName` | string | no | Display name of the kit. | | `line.productKit.customPricing` | boolean | no | Whether the kit is custom-priced (true) rather than computed from its components (false). | | `line.productKit.description` | string | no | Free-text description of the kit. | | `line.productKit.displayKitItems` | object[] | no | Which documents and views show the kit's components, as a JSON array of display-target keys. The keys are "displayOnAllViews", "displayOnWorkOrderPdf", "displayOnDeliveryDocketPdf", "displayOnInvoicePdf", "displayOnQuotePdf", "displayOnProformaInvoicePdf", "displayOnCustomerOnlineView" and "displayOnAccountingPackage"; a key that is not one of these matches no document. Optional: when omitted, the kit takes your company's "Display individual Product Kit items on" setting, as a kit built in the Factory app does. An empty array shows the components on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `line.productKit.displaySubKits` | object[] | no | Which documents and views show the kit's sub-kits, as a JSON array of the same display-target keys as `displayKitItems`. Optional: when omitted, the kit takes your company's "Display Product Kit sub-kits on" setting, as a kit built in the Factory app does. An empty array shows the sub-kits on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `line.productKit.pricing` | LinePricing | no | Overall pricing for the kit line. Its `isTaxFree` is read before the components': a tax-free kit line exempts the whole kit whatever the components say, a taxable one lets each component's own `isTaxFree` decide, and a custom-priced kit is taxed on the kit line alone. Omit it and it depends on the kit: on a standard kit it is false, with the components following the customer, so a component you mark taxable is charged even for an exempt customer; on a custom-priced kit it follows the customer, so an exempt customer's custom kit is exempt in full. A value you send, true or false, is kept as sent. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `line.productKit.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `line.productKit.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `line.productKit.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `line.productKit.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `line.productKit.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `line.productKit.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `line.productKit.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `line.productKit.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `line.productKit.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.productKit.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `line.productKit.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `line.productKit.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `line.productKit.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.attributes` | object[] | no | Additional named attributes for the kit line, as a JSON array of {name, value} entries; for measurement-priced kits they describe the measurement entries. | | `line.productKit.components` | KitComponent[] | no | The kit's direct components. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `line.productKit.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `line.productKit.components.productId` | string | no | The catalogue product id for this component. Optional. | | `line.productKit.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `line.productKit.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `line.productKit.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `line.productKit.components.productDescription` | string | no | Free-text description of the component. | | `line.productKit.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `line.productKit.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `line.productKit.components.unitPrice` | Money | no | Price per unit. | | `line.productKit.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `line.productKit.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `line.productKit.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `line.productKit.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `line.productKit.components.cost` | Money | no | Cost of the component. | | `line.productKit.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `line.productKit.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `line.productKit.components.margin` | Money | no | Margin earned on the component. | | `line.productKit.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.productKit.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `line.productKit.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `line.productKit.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `line.productKit.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `line.productKit.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `line.productKit.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `line.productKit.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `line.productKit.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `line.productKit.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `line.productKit.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.subKits` | SubKit[] | no | The kit's sub-assemblies, each with its own components. A sub-assembly within a kit. A sub-kit groups a set of components under its own title and quantity, letting a kit be organized into named sub-assemblies. | | `line.productKit.subKits.title` | string | no | Display title of the sub-assembly. | | `line.productKit.subKits.quantity` | string | no | Quantity of this sub-assembly, as a decimal string. | | `line.productKit.subKits.actualQuantity` | string | no | Actual measured quantity, as a decimal string. | | `line.productKit.subKits.unitPrice` | Money | no | Price per unit of the sub-assembly. | | `line.productKit.subKits.totalPrice` | Money | no | Total price of the sub-assembly. | | `line.productKit.subKits.components` | KitComponent[] | no | The components that make up this sub-assembly. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `line.productKit.subKits.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `line.productKit.subKits.components.productId` | string | no | The catalogue product id for this component. Optional. | | `line.productKit.subKits.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `line.productKit.subKits.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `line.productKit.subKits.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `line.productKit.subKits.components.productDescription` | string | no | Free-text description of the component. | | `line.productKit.subKits.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `line.productKit.subKits.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `line.productKit.subKits.components.unitPrice` | Money | no | Price per unit. | | `line.productKit.subKits.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `line.productKit.subKits.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `line.productKit.subKits.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `line.productKit.subKits.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `line.productKit.subKits.components.cost` | Money | no | Cost of the component. | | `line.productKit.subKits.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `line.productKit.subKits.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `line.productKit.subKits.components.margin` | Money | no | Margin earned on the component. | | `line.productKit.subKits.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `line.productKit.subKits.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `line.productKit.subKits.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `line.productKit.subKits.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `line.productKit.subKits.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `line.productKit.subKits.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `line.productKit.subKits.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `line.productKit.subKits.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `line.productKit.subKits.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `line.productKit.subKits.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `line.productKit.subKits.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.subKits.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.subKits.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.subKits.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.productKit.subKits.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `line.productKit.subKits.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `line.productKit.subKits.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `line.productKit.subKits.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `line.id` | string | no | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `AddQuoteLineItemResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, or the quote has been accepted and converted to an order — manage its lines with the OrderService line endpoints, swapping the id's quote_ prefix for order_ (the 26-character suffix stays the same). | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the quote it updated), its outcome is still unknown, or the key was reused with a materially different body. Verify the quote's lines with GetQuote, then send a new `requestId` to add a new line. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `AddQuoteLineItemResponse` | Field | Type | Description | | --- | --- | --- | | `quote` | Quote | The quote's id and recomputed totals (`subtotal`, `taxAmount`, `total`, `totalCost`, `margin`, `labourTotal`). No other field is populated — `lines` is empty and `isSubmitted` is absent regardless of the quote's state — so read the quote with GetQuote for the new line's id and position. | | `quote.quoteId` | string | The quote's unique id. Read-only. | | `quote.isSubmitted` | boolean | Whether the quote has been finalised and sent (true) or is still a draft (false). | | `quote.customerId` | string | The id of the customer this quote belongs to. Always present. Read-only. | | `quote.customerCompanyName` | string | The customer's company name. Read-only. | | `quote.subtotal` | Money | Quote subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `quote.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.taxAmount` | Money | Total tax for the quote, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `quote.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.total` | Money | Quote grand total. Derived by the server; read-only. | | `quote.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.margin` | Money | Quote margin, computed by the server from the line items. Read-only. | | `quote.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.totalCost` | Money | Total cost of the quote, computed by the server from the line items. Read-only. | | `quote.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.discountAmount` | Money | The quote-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `quote.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.lines` | SalesLine[] | The quote's line items. | | `quote.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `quote.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `quote.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `quote.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `quote.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `quote.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `quote.lines.flashing.productName` | string | The display name of the flashing material. | | `quote.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `quote.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `quote.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `quote.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `quote.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `quote.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `quote.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `quote.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `quote.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `quote.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `quote.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `quote.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `quote.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `quote.adjustments` | Adjustment[] | Quote-level fees, discounts, and markups. At most one fee per quote is allowed. | | `quote.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `quote.adjustments.title` | string | A label describing the adjustment. | | `quote.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `quote.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `quote.fulfilmentMethod` | FulfilmentMethod | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `quote.billingAddress` | Address | Billing address for the quote. | | `quote.billingAddress.address1` | string | First address line (street number and name). | | `quote.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.billingAddress.city` | string | City or suburb. | | `quote.billingAddress.state` | string | State, province, or region. | | `quote.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `quote.deliveryAddress.address1` | string | First address line (street number and name). | | `quote.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.deliveryAddress.city` | string | City or suburb. | | `quote.deliveryAddress.state` | string | State, province, or region. | | `quote.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `quote.installAddress.address1` | string | First address line (street number and name). | | `quote.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.installAddress.city` | string | City or suburb. | | `quote.installAddress.state` | string | State, province, or region. | | `quote.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryFee` | Money | The delivery fee quoted on the order. Read-only. | | `quote.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.reference` | string | Free-text external reference, e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. | | `quote.orderNumber` | string | Factory's number for this quote. Assigned by the server when the quote is created, sequential within your company, and never changed — the quote keeps this number if it becomes an order. Shown as "Quote #" on the quote PDF and email, as "Order #" in the Factory app (order list, order page, workflow board) and on invoices and delivery documents, and used as the document reference in the accounting integrations. Read-only. | | `quote.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `quote.contact` | Contact | The quote's point-of-contact person. | | `quote.contact.name` | string | The contact person's name. | | `quote.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `quote.contact.phone` | string | The contact's landline phone number. | | `quote.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `quote.notes` | string | Free-text notes about the quote. | | `quote.requiredAt` | string | The date the customer needs the order by, if the quote is accepted. | | `quote.quotedAt` | string | The date the quote was issued. | | `quote.createdAt` | string | When the quote was created. Read-only. | | `quote.submittedAt` | string | When the quote was sent (finalised). Read-only. | | `quote.lastUpdatedAt` | string | When the quote was last updated. Read-only. | | `quote.createdByUserId` | string | The id of the user who created the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.submittedByUserId` | string | The id of the user who sent the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.tax` | TaxDetail | The tax descriptor for the quote: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `quote.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `quote.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `quote.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `quote.labels` | Label[] | The labels attached to this quote, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `quote.labels.labelId` | string | The label's id. | | `quote.labels.name` | string | Display name of the label. Unique within your company. | | `quote.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `quote.labourTotal` | Money | The total charged for labour on the quote: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `quote.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `drawings` | DrawingIdMapping[] | When the added line was a flashing line: the server identities of its created drawings (your `tempId` paired with the assigned `drawingId`, the other sides in the order you sent them) — use them to address UploadDrawingSvg. Empty for every other line kind. | | `drawings.tempId` | string | The `tempId` you supplied on the drawing, echoed verbatim; empty when you did not supply one (position in the list still identifies the drawing). | | `drawings.drawingId` | string | The server-assigned id of the drawing. | | `drawings.otherSides` | DrawingIdMapping[] | The identities of the drawing's other sides, in the order you sent them. | | `drawings.otherSides.tempId` | string | The `tempId` you supplied on the drawing, echoed verbatim; empty when you did not supply one (position in the list still identifies the drawing). | | `drawings.otherSides.drawingId` | string | The server-assigned id of the drawing. | | `drawings.otherSides.otherSides` | DrawingIdMapping[] | The identities of the drawing's other sides, in the order you sent them. | --- # PUT /v1/quotes/{quoteId}/lines/{lineId} **Service:** Quotes **Operation:** `QuoteService_UpdateQuoteLineItem` Send the full replacement line content; the whole line is replaced and the quote's totals are recomputed. Identify the line by the server-assigned id returned when you read the quote. A line id that does not belong to the quote is rejected with HTTP 404 (not_found). A flashing line cannot yet be replaced and is rejected with HTTP 501 (not_implemented). Concurrent writes to the same line are not detected: the last write wins. Re-read the quote after writing if other clients may be editing it. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote the line belongs to, as returned by CreateQuote. | | `lineId` | path | string | yes | The server-assigned id of the line to replace, as returned when the quote is read. This has a different form from the quote id. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Request body `SalesLine` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `onTheFly` | OnTheFlyLine | no | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `onTheFly.productName` | string | yes | Name of the item. Required. | | `onTheFly.productDescription` | string | no | Optional longer description of the item. | | `onTheFly.colour` | string | no | Optional colour for the item. | | `onTheFly.pricing` | LinePricing | no | Pricing for this line. Unit price is required; the line total is required until server-side derivation is enabled for your account (see `totalPrice` on LinePricing), except on custom-formula lines, where it is always required. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `onTheFly.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `onTheFly.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `onTheFly.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `onTheFly.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `onTheFly.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `onTheFly.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `onTheFly.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `onTheFly.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `onTheFly.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `onTheFly.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `onTheFly.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `onTheFly.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `onTheFly.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `onTheFly.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `onTheFly.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `onTheFly.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `onTheFly.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `onTheFly.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `onTheFly.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `onTheFly.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `onTheFly.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. | | `catalogue` | CatalogueLine | no | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `catalogue.productId` | string | yes | The id of the catalogue product for this line. Required, and must match an existing catalogue product; an unknown id rejects the whole request. | | `catalogue.productRowId` | string | no | Optional id of a specific row within the catalogue product. It must be one of the product's rows. The row supplies the line's attributes, cost and unit price when you leave those out. | | `catalogue.rowPriceId` | string | no | Optional id selecting the row's price at one of your price levels — the `rowPriceId` returned by the catalogue (not the price level's own id). It must be a price of `productRowId`; sent without `productRowId`, it must be a price of one of the product's rows, and that row becomes the line's. When you send no `unitPrice`, the line is priced at this level. | | `catalogue.productName` | string | no | Display name for the line. Defaults to the product's name. | | `catalogue.productDescription` | string | no | Longer description for the line. | | `catalogue.categoryName` | string | no | Category name for the line. Defaults to the product's category. | | `catalogue.priceName` | string | no | Price name shown for the line. | | `catalogue.params` | object[] | no | Product parameters as a JSON array of {name, value} entries (for example [{"name": "Colour", "value": "Monument"}]). The colour is taken from the "Colour" parameter and validated against the product's available colours. | | `catalogue.attributes` | object[] | no | Additional named attributes for the line, as a JSON array of {name, value} entries (for example {name: "Size", value: "76x38mm"}). An entry may also carry flags that hide it from specific documents or views, such as the invoice PDF or the customer view. Defaults to the row's attributes when the line names a row. | | `catalogue.pricing` | LinePricing | no | Pricing for this line. Parts you leave out come from the catalogue: `pricingStrategy` from the product; `cost` from the row; `unitPrice` from the row's price at `rowPriceId`, or the row's marked-up price (with the row's `markup`) when no price level is chosen. A line that names no row takes only the strategy, and `unitPrice` is yours to send. An omitted `isTaxFree` follows the customer and the product's tax-free setting (see `isTaxFree`). `quantity`, measurements and `totalPrice` follow the rules of every priced line. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `catalogue.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `catalogue.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `catalogue.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `catalogue.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `catalogue.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `catalogue.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `catalogue.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `catalogue.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `catalogue.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `catalogue.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `catalogue.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `catalogue.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `catalogue.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `catalogue.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `catalogue.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `catalogue.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `catalogue.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `catalogue.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `catalogue.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `catalogue.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `labour` | LabourLine | no | A line charging for labour. Set exactly one of the six line-type fields. | | `labour.labourUserId` | string | yes | The id of the worker who performed the labour. Required, and must be an active user in the company that owns the order or quote. | | `labour.productName` | string | no | A short name for the labour. | | `labour.productDescription` | string | no | A longer description of the labour. | | `labour.pricing` | LinePricing | no | Quantity and pricing for this line: the hours in `quantity`, the charge rate in `unitPrice` and the cost rate in `cost`. There are no rate overrides elsewhere on the line, and the worker's own rates are not applied: an omitted `cost` is stored as zero, not taken from the user's `hourlyRateCost`. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `labour.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `labour.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `labour.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `labour.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `labour.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `labour.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `labour.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `labour.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `labour.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `labour.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `labour.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `labour.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `labour.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `labour.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `labour.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `labour.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `labour.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `labour.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `labour.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `labour.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `notes` | NotesLine | no | A note line, with no pricing. Set exactly one of the six line-type fields. | | `notes.notesType` | NotesType | yes | The kind of note. Required. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `notes.notes` | string | no | The note text. | | `notes.productName` | string | no | A short name for the note line. | | `notes.productDescription` | string | no | A longer description for the note line. | | `flashing` | FlashingLine | no | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `flashing.templateId` | string | no | The id of the flashing material or template. Required when the order or quote is submitted. | | `flashing.productName` | string | no | The display name of the flashing material. | | `flashing.priceLevel` | string | no | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `flashing.colour` | string | no | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `flashing.thickness` | string | no | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `flashing.bends` | string | no | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `flashing.totalGirth` | string | no | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `flashing.totalLength` | string | no | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `flashing.unitPrice` | Money | no | The price per metre of the flashing. Required when the order or quote is submitted. | | `flashing.unitPrice.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.unitPrice.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.totalPrice` | Money | no | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `flashing.totalPrice.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.totalPrice.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.customPrice` | Money | no | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `flashing.customPrice.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.customPrice.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.customPricePerLength` | Money | no | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `flashing.customPricePerLength.amountMicros` | string | no | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `flashing.customPricePerLength.currency` | string | no | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `flashing.subitems` | object[] | no | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `flashing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `flashing.drawing` | FlashingDrawing | yes | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `flashing.drawing.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `flashing.drawing.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `flashing.drawing.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `flashing.drawing.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `flashing.drawing.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `flashing.drawing.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `flashing.drawing.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `flashing.drawing.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `flashing.drawing.points.*.id` | string | no | The unique id of this point within the drawing. | | `flashing.drawing.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `flashing.drawing.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `flashing.drawing.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `flashing.drawing.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `flashing.drawing.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `flashing.drawing.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `flashing.drawing.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `flashing.drawing.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `flashing.drawing.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `flashing.drawing.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.drawing.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.drawing.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.drawing.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `flashing.drawing.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `flashing.drawing.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `flashing.drawing.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `flashing.drawing.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `flashing.drawing.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `flashing.drawing.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.drawing.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.drawing.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.drawing.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.drawing.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.drawing.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `flashing.drawing.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `flashing.drawing.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `flashing.drawing.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `flashing.drawing.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.drawing.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.drawing.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.drawing.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.drawing.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.drawing.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `flashing.drawing.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.drawing.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.drawing.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `flashing.drawing.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `flashing.drawing.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `flashing.drawing.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `flashing.drawing.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `flashing.drawing.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `flashing.drawing.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `flashing.drawing.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `flashing.drawing.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `flashing.drawing.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.drawing.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.drawing.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `flashing.drawing.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `flashing.drawing.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.drawing.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.drawing.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `flashing.otherSides` | FlashingDrawing[] | no | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `flashing.otherSides.drawingId` | string | no | Server-assigned unique id of the drawing. Read-only output. | | `flashing.otherSides.tempId` | string | no | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `flashing.otherSides.drawingNumber` | integer | no | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `flashing.otherSides.side` | DrawingSide | no | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `flashing.otherSides.isFreeDrawing` | boolean | no | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `flashing.otherSides.isLargeBoxSize` | boolean | no | Whether the drawing uses the large box size. | | `flashing.otherSides.isDeleted` | boolean | no | Whether the drawing has been deleted. | | `flashing.otherSides.points` | map | no | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `flashing.otherSides.points.*.id` | string | no | The unique id of this point within the drawing. | | `flashing.otherSides.points.*.x` | number | no | The x coordinate of the point, in canvas space. | | `flashing.otherSides.points.*.y` | number | no | The y coordinate of the point, in canvas space. | | `flashing.otherSides.points.*.vectors` | string[] | no | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `flashing.otherSides.points.*.connect` | string[] | no | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `flashing.otherSides.points.*.finish` | Finish | no | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `flashing.otherSides.points.*.finish.type` | FinishType | no | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `flashing.otherSides.points.*.finish.size` | number | no | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `flashing.otherSides.points.*.finish.label` | string | no | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `flashing.otherSides.points.*.finish.flip` | boolean | no | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `flashing.otherSides.points.*.finish.position` | LabelPosition | no | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.otherSides.points.*.finish.position.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.otherSides.points.*.finish.position.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.otherSides.points.*.finish.position.width` | number | no | The width of the label box, in canvas space. | | `flashing.otherSides.points.*.finish.position.height` | number | no | The height of the label box, in canvas space. | | `flashing.otherSides.lines` | LineSet | no | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `flashing.otherSides.lines.values` | map | no | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `flashing.otherSides.lines.values.*.size` | number | no | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `flashing.otherSides.lines.values.*.hidden` | boolean | no | Whether this edge's length label is hidden. | | `flashing.otherSides.lines.positions` | map | no | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.otherSides.lines.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.otherSides.lines.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.otherSides.lines.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.otherSides.lines.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.otherSides.angles` | AngleSet | no | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `flashing.otherSides.angles.values` | map | no | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `flashing.otherSides.angles.values.*.angle` | number | no | The angle at this vertex, in degrees. Omitted when no angle is set. | | `flashing.otherSides.angles.values.*.hidden` | boolean | no | Whether this vertex's angle label is hidden. Omitted when not set. | | `flashing.otherSides.angles.positions` | map | no | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `flashing.otherSides.angles.positions.*.x` | number | no | The x coordinate of the label box, in canvas space. | | `flashing.otherSides.angles.positions.*.y` | number | no | The y coordinate of the label box, in canvas space. | | `flashing.otherSides.angles.positions.*.width` | number | no | The width of the label box, in canvas space. | | `flashing.otherSides.angles.positions.*.height` | number | no | The height of the label box, in canvas space. | | `flashing.otherSides.annotations` | map | no | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `flashing.otherSides.annotations.*.x` | number | no | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.otherSides.annotations.*.y` | number | no | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `flashing.otherSides.annotations.*.width` | number | no | The width of the annotation box, in canvas space. | | `flashing.otherSides.annotations.*.height` | number | no | The height of the annotation box, in canvas space. | | `flashing.otherSides.annotations.*.value` | string | no | The annotation text as a flat string. May be present alongside the structured `text` form. | | `flashing.otherSides.annotations.*.text` | TextRow[] | no | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `flashing.otherSides.annotations.*.text.id` | integer | no | The 0-based index of this row within the text annotation. | | `flashing.otherSides.annotations.*.text.indent` | integer | no | The indentation level of this row. | | `flashing.otherSides.annotations.*.text.value` | string[] | no | The text segments that make up this row, in order. | | `flashing.otherSides.annotations.*.rotateDeg` | number | no | Optional rotation of the annotation, in degrees. | | `flashing.otherSides.frontArrow` | Arrow | no | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `flashing.otherSides.frontArrow.x` | number | no | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.otherSides.frontArrow.y` | number | no | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `flashing.otherSides.frontArrow.angle` | number | no | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `flashing.otherSides.squareAngle` | SquareAngle | no | The right-angle (90°) marker placed on the drawing. | | `flashing.otherSides.squareAngle.x` | number | no | The x coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.otherSides.squareAngle.y` | number | no | The y coordinate of the marker, in canvas space. Omitted until placed. | | `flashing.otherSides.svgUrl` | string | no | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `flashing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `flashing.tax.rate` | string | no | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `flashing.tax.code` | string | no | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `flashing.tax.jurisdiction` | string | no | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `productKit` | ProductKitLine | no | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `productKit.kitId` | string | no | The kit's id (`kitId` on Kit, from the catalogue read surface). Optional: omit it to define an on-the-fly kit not linked to any catalogue kit — the kit is described entirely by this line's name, pricing, components, and sub-kits. | | `productKit.kitRowId` | string | no | The selected kit row/variant id. Optional. | | `productKit.productName` | string | no | Display name of the kit. | | `productKit.customPricing` | boolean | no | Whether the kit is custom-priced (true) rather than computed from its components (false). | | `productKit.description` | string | no | Free-text description of the kit. | | `productKit.displayKitItems` | object[] | no | Which documents and views show the kit's components, as a JSON array of display-target keys. The keys are "displayOnAllViews", "displayOnWorkOrderPdf", "displayOnDeliveryDocketPdf", "displayOnInvoicePdf", "displayOnQuotePdf", "displayOnProformaInvoicePdf", "displayOnCustomerOnlineView" and "displayOnAccountingPackage"; a key that is not one of these matches no document. Optional: when omitted, the kit takes your company's "Display individual Product Kit items on" setting, as a kit built in the Factory app does. An empty array shows the components on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `productKit.displaySubKits` | object[] | no | Which documents and views show the kit's sub-kits, as a JSON array of the same display-target keys as `displayKitItems`. Optional: when omitted, the kit takes your company's "Display Product Kit sub-kits on" setting, as a kit built in the Factory app does. An empty array shows the sub-kits on no document. On an update the array you send replaces the stored one, and omitting it applies the company setting again. | | `productKit.pricing` | LinePricing | no | Overall pricing for the kit line. Its `isTaxFree` is read before the components': a tax-free kit line exempts the whole kit whatever the components say, a taxable one lets each component's own `isTaxFree` decide, and a custom-priced kit is taxed on the kit line alone. Omit it and it depends on the kit: on a standard kit it is false, with the components following the customer, so a component you mark taxable is charged even for an exempt customer; on a custom-priced kit it follows the customer, so an exempt customer's custom kit is exempt in full. A value you send, true or false, is kept as sent. Pricing for a single priced line: quantity, prices, discount, cost, markup, margin, and pricing strategy. On priced lines the server validates — or, once derivation is enabled for your account, derives — the line total from the quantity, unit price, and discount; see `totalPrice` for the exact formula, the rounding rule, and what happens if it does not match. | | `productKit.pricing.quantity` | string | no | Quantity for this line, as a decimal string (e.g. "2" or "2.5"). Supports up to 4 decimal places. It is the first factor of the line-total formula documented on `totalPrice` — EXCEPT on measurement-priced (lineal/square) lines, where the measured length or area alone sets the priced amount and this factor is treated as 1 whenever the server derives or checks the total. Do not multiply a measurement-priced total by quantity. (Only on free-text lines on accounts WITHOUT server-side derivation does the legacy formula still multiply by it — see `totalPrice`). | | `productKit.pricing.unitPrice` | Money | no | Per-unit price. Required on priced lines, with two exceptions. On a catalogue line that names a row (`productRowId` or `rowPriceId`) an omitted price is the row's price at that level, or the row's marked-up price when no level is chosen. And with server-side derivation enabled for your account you may omit it and supply `cost` plus `markup` instead: the server computes it as cost x (1 + markup/100) (or cost + markup for a fixed markup). If you can send the price directly, do; the derivation exists for callers whose source system stores only cost and markup. The server rounds this to 4 decimal places (half up) before pricing, so unit-price precision finer than that (micro amounts that are not a multiple of 100) is not preserved and can shift the computed line total. | | `productKit.pricing.totalPrice` | Money | no | The line total, as a Money amount in micros. Server-side derivation is rolling out account by account. Once it is enabled for your account, this field is optional on most priced lines — omit it (leave the whole Money object out; an empty Money is a supplied $0.00) and the server computes it. Until then it is required on priced lines, exactly as before. The formula is: quantity * `unitPrice` * (1 - discount / 100) rounded to the nearest cent — 2 decimal places, half up (0.005 rounds up). For lineal or square measurement pricing the result is then multiplied by the measured length or area the server derives from the line's measurement details before rounding — and on measurement-priced lines the quantity factor is pinned to 1 once derivation is enabled for your account, whether the server derives the total or checks one you send: the measurements alone set the priced amount. (Without derivation, free-text lines still multiply by `quantity` — match it when computing such a total yourself.) Deriving needs a non-zero quantity or measurement set (deriving with a zero quantity or an empty measurement set is rejected rather than stored as $0.00; a zero unit price — a free line — still derives). On a product kit priced as a standard kit, an omitted parent total is derived as the sum of its components and sub-kits (omitted sub-kit totals are likewise derived from their components) — priced component totals must accompany the request; a missing one is rejected rather than summed as zero. A supplied kit total, at any tier, is stored as sent. If you DO send a value on a catalogue (once derivation is enabled for your account), on-the-fly, or labour line, it is checked against the formula and a mismatch is rejected with HTTP 400. Send the amount as a whole number of cents (`amountMicros` a multiple of 10000); a sub-cent amount is rounded to the nearest cent before the check. Exceptions, where the total is never derived — send a correct value, because these lines are stored as sent: | | `productKit.pricing.discount` | string | no | Discount applied to the line, as a decimal percentage string (e.g. "10.00" means 10%). Supports up to 2 decimal places. In the line-total formula on `totalPrice` it enters as the factor (1 - discount / 100). | | `productKit.pricing.cost` | Money | no | Unit cost for this line, used to compute margin — and, with derivation enabled, the base the server recomputes `markup` (and an omitted `unitPrice`) from. Rounded to 4 decimal places (half up) before use, so sub-4dp cost precision is not preserved. | | `productKit.pricing.markup` | string | no | Markup applied to the line, as a decimal string — either a money amount or a percentage, depending on `markupIsPercentage`. With server-side derivation enabled for your account: whenever `cost` is non-zero, the value you send here is DISCARDED and recomputed from `cost` and the unit price (as a percentage — `markupIsPercentage` reads back true), so the stored markup always agrees with the stored prices. Do not expect to read back the value you sent. With a zero or absent cost your value is kept as sent. | | `productKit.pricing.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or a fixed amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads, and reads back true whenever the server recomputed the markup (see `markup`). | | `productKit.pricing.margin` | Money | no | Margin for the line. When supplied, it is validated against cost and quantity — and with derivation enabled it is recomputed alongside the total whenever `cost` is present, so (like `markup`) the stored value may differ from what you sent. Simplest: omit it and let the server compute. | | `productKit.pricing.pricingStrategy` | PricingStrategy | no | How this line is priced (per-unit quantity, lineal, or square measurement). Defaults to per-unit quantity pricing. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `productKit.pricing.priceLevel` | string | no | Optional named price level applied to this line. | | `productKit.pricing.isTaxFree` | boolean | no | Whether this line is exempt from tax. Omit it and the line follows the customer: a customer with `disableTaxByDefault` makes the line exempt, and on a catalogue line so does a product marked tax-free in the catalogue; otherwise the line is taxed at your account's rate. A value you send, true or false, is kept as sent. On an update the same rule applies: an omitted flag follows the customer again rather than keeping the stored value. Always present on reads. | | `productKit.pricing.tax` | TaxDetail | no | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `productKit.pricing.measurements` | MeasurementEntry[] | no | The measurements that price this line, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this same object. On these strategies the measured amount is the priced quantity — `quantity` is NOT a factor (on accounts without server-side derivation, free-text lines are the one legacy exception; see `quantity`); the server sums these pieces (applying your account's minimum-length setting on lineal strategies) to derive the amount that scales the line total. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.pricing.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.pricing.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.pricing.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.pricing.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the line price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.pricing.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.pricing.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.pricing.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.attributes` | object[] | no | Additional named attributes for the kit line, as a JSON array of {name, value} entries; for measurement-priced kits they describe the measurement entries. | | `productKit.components` | KitComponent[] | no | The kit's direct components. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `productKit.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `productKit.components.productId` | string | no | The catalogue product id for this component. Optional. | | `productKit.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `productKit.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `productKit.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `productKit.components.productDescription` | string | no | Free-text description of the component. | | `productKit.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `productKit.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `productKit.components.unitPrice` | Money | no | Price per unit. | | `productKit.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `productKit.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `productKit.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `productKit.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `productKit.components.cost` | Money | no | Cost of the component. | | `productKit.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `productKit.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `productKit.components.margin` | Money | no | Margin earned on the component. | | `productKit.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `productKit.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `productKit.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `productKit.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `productKit.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `productKit.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `productKit.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `productKit.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `productKit.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `productKit.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `productKit.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.subKits` | SubKit[] | no | The kit's sub-assemblies, each with its own components. A sub-assembly within a kit. A sub-kit groups a set of components under its own title and quantity, letting a kit be organized into named sub-assemblies. | | `productKit.subKits.title` | string | no | Display title of the sub-assembly. | | `productKit.subKits.quantity` | string | no | Quantity of this sub-assembly, as a decimal string. | | `productKit.subKits.actualQuantity` | string | no | Actual measured quantity, as a decimal string. | | `productKit.subKits.unitPrice` | Money | no | Price per unit of the sub-assembly. | | `productKit.subKits.totalPrice` | Money | no | Total price of the sub-assembly. | | `productKit.subKits.components` | KitComponent[] | no | The components that make up this sub-assembly. One component line inside a kit or sub-kit. A component is itself typed — it may be a catalogue-product, on-the-fly, notes, labour, or kit-product line — and carries its own pricing and measurement details. | | `productKit.subKits.components.productType` | KitComponentType | no | The component's kind: catalogue product, on-the-fly, labour, or notes. Required — it determines which of the fields below apply. The kind of a component within a product kit. - KIT_COMPONENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a kit component. - KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT: A component that is a product from your catalogue. - KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT: A free-form component not tied to a catalogue product. Occurs on order and quote kits only — it is not a catalogue-template component kind. - KIT_COMPONENT_TYPE_NOTES: A note component: carries text rather than a priced product. - KIT_COMPONENT_TYPE_LABOUR: A labour component. One of: KIT_COMPONENT_TYPE_UNSPECIFIED, KIT_COMPONENT_TYPE_CATALOGUE_PRODUCT, KIT_COMPONENT_TYPE_ON_THE_FLY_PRODUCT, KIT_COMPONENT_TYPE_NOTES, KIT_COMPONENT_TYPE_LABOUR. | | `productKit.subKits.components.productId` | string | no | The catalogue product id for this component. Optional. | | `productKit.subKits.components.productRowId` | string | no | The catalogue product table-row (variant) id. Optional. | | `productKit.subKits.components.kitProductId` | string | no | The catalogue kit-product-component id this component was instantiated from. Optional. | | `productKit.subKits.components.productName` | string | no | Display name of the component. Set it on every write: component names are stored verbatim and are NEVER derived from `productId` or `productRowId` — a component created without a name is stored nameless and every read of the document returns it with an empty name. When building this line from a catalogue kit template, echo the template component's `productName` (returned by GetKit) into this field. | | `productKit.subKits.components.productDescription` | string | no | Free-text description of the component. | | `productKit.subKits.components.quantity` | string | no | Quantity for this component, as a decimal string. | | `productKit.subKits.components.baseQuantity` | string | no | Base quantity before any per-unit multipliers, as a decimal string. | | `productKit.subKits.components.unitPrice` | Money | no | Price per unit. | | `productKit.subKits.components.totalPrice` | Money | no | Total price for this component. With derived pricing enabled every priced component must carry it — a missing one is rejected rather than summed as zero — and it is checked against the component's own figures: unit price x quantity x (1 - discount / 100), with the measured amount replacing quantity on measurement-priced components (`quantity` is not a factor there — see `measurements`). | | `productKit.subKits.components.customPrice` | Money | no | A manual override of the component's TOTAL price — not a unit price. When set, tax and margin are computed from this value in place of `totalPrice`, so a disagreeing pair stores an incoherent document. Send it only to deliberately override the component's total, and never echo `unitPrice` into it. With derived pricing enabled, a `customPrice` that disagrees with `totalPrice` is rejected (400). | | `productKit.subKits.components.customPricePerLength` | Money | no | A manual override of the price per unit of measured length (for example, per lineal metre), applied instead of the unit price on measurement-priced components. Send it only to deliberately override — omit it otherwise: an explicit $0.00 is stored as an override of $0.00, not ignored. | | `productKit.subKits.components.discount` | string | no | Discount applied to this component, as a decimal string. | | `productKit.subKits.components.cost` | Money | no | Cost of the component. | | `productKit.subKits.components.markup` | string | no | Markup applied to the component, as a decimal string. Interpreted as a percentage when `markupIsPercentage` is true. | | `productKit.subKits.components.markupIsPercentage` | boolean | no | Whether markup is a percentage (true) or an absolute amount (false). Omit it to use the default, true; send false only for a fixed-amount markup. Always present on reads. | | `productKit.subKits.components.margin` | Money | no | Margin earned on the component. | | `productKit.subKits.components.pricingStrategy` | PricingStrategy | no | How the component is priced. How a quantity is interpreted for pricing — whether it means a count of each, a length, or an area. On a catalogue product this is the product's pricing basis. On an order or quote line, measurement-based strategies (lineal or square metres/feet) require the line's measurement details to be supplied. - PRICING_STRATEGY_UNSPECIFIED: Unset, or a legacy product with no pricing strategy recorded. - PRICING_STRATEGY_BASIC_QUANTITIES: Quantity is a simple per-each count. - PRICING_STRATEGY_LINEAL_METRES: Quantity is a length in lineal metres. - PRICING_STRATEGY_CUSTOM_FORMULA: Quantity is priced by a custom formula. - PRICING_STRATEGY_SQUARE_METRES: Quantity is an area in square metres. - PRICING_STRATEGY_LINEAL_FEET: Quantity is a length in lineal feet. - PRICING_STRATEGY_SQUARE_FEET: Quantity is an area in square feet. One of: PRICING_STRATEGY_UNSPECIFIED, PRICING_STRATEGY_BASIC_QUANTITIES, PRICING_STRATEGY_LINEAL_METRES, PRICING_STRATEGY_CUSTOM_FORMULA, PRICING_STRATEGY_SQUARE_METRES, PRICING_STRATEGY_LINEAL_FEET, PRICING_STRATEGY_SQUARE_FEET. | | `productKit.subKits.components.isTaxFree` | boolean | no | Whether this component is exempt from tax. Omit it and the component follows the customer: a customer with `disableTaxByDefault` makes it exempt; otherwise it is taxed at your account's rate. A value you send, true or false, is kept as sent, and an update that omits it follows the customer again. It counts while the kit line's own `isTaxFree` is false and the kit is not custom-priced — the usual case, since an omitted kit-line flag on a standard kit is false; a kit line sent tax-free exempts the whole kit, and a custom-priced kit is taxed on its line alone. A component's own catalogue product tax setting is not applied in this version — send `isTaxFree` on the component for that. Always present on reads. | | `productKit.subKits.components.attributes` | object[] | no | Additional named attributes for the component, as a JSON array of {name, value} entries; for measurement-priced components they describe the measurement entries. | | `productKit.subKits.components.notes` | string | no | Free-text notes — used when the component is a notes line. | | `productKit.subKits.components.notesType` | NotesType | no | The kind of notes line, when this is a notes component. Whether a notes line is internal-only or visible to the customer. - NOTES_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice for a notes line. - NOTES_TYPE_INTERNAL: An internal note, not shown to the customer. - NOTES_TYPE_EXTERNAL: An external note, visible to the customer. One of: NOTES_TYPE_UNSPECIFIED, NOTES_TYPE_INTERNAL, NOTES_TYPE_EXTERNAL. | | `productKit.subKits.components.labourUserId` | string | no | The company user assigned, when this is a labour component. | | `productKit.subKits.components.colour` | string | no | Colour of the component, populated for catalogue components from the product row. | | `productKit.subKits.components.material` | string | no | Material of the component, populated for catalogue components from the product row. | | `productKit.subKits.components.colourOptions` | object[] | no | The selectable options for this component's variant attribute — most often colour, but also other attributes such as size — as a JSON array of {label, value} entries, echoed from the catalogue. Display and read only. | | `productKit.subKits.components.tax` | TaxDetail | no | The tax applied to this component: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable components; this object reserves the shape for richer tax later. | | `productKit.subKits.components.measurements` | MeasurementEntry[] | no | The measurements that price this component, for the lineal and square pricing strategies. Units follow the strategy: metres for *_METRES, feet for *_FEET (see MeasurementEntry). Requires an explicit, measurement-priced `pricingStrategy` on this component. The measured amount is the priced quantity — `quantity` is NOT a factor. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.subKits.components.measurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.subKits.components.measurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.subKits.components.measurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `productKit.subKits.components.asBuiltMeasurements` | MeasurementEntry[] | no | As-built measurements — what production actually cut, in the same shape and units as `measurements`. These drive cost, margin, and stock consumption, never the component price. Optional: whenever omitted (create and update alike) they default to `measurements`. Send them only when your as-built figures differ from what was quoted. One measured piece (or panel) in a measurement-priced line or kit component. Values are decimal strings in the line's strategy-native unit: metres for the *_METRES pricing strategies, feet for *_FEET. | | `productKit.subKits.components.asBuiltMeasurements.length` | string | no | Length of this piece, as a decimal string in the strategy's unit (e.g. "2.4" metres, or "8" feet). | | `productKit.subKits.components.asBuiltMeasurements.width` | string | no | Width of this piece, as a decimal string in the strategy's unit. Square strategies only — a width on a lineal entry is rejected, and a square entry without one is rejected. | | `productKit.subKits.components.asBuiltMeasurements.amount` | string | no | How many pieces of this size, as a decimal string. Defaults to 1. | | `id` | string | no | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `UpdateQuoteLineItemResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, the quote has been accepted and converted to an order — manage its lines with the OrderService line endpoints — or the line id does not identify a line on this quote. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `UpdateQuoteLineItemResponse` | Field | Type | Description | | --- | --- | --- | | `quote` | Quote | The updated quote, including the recomputed totals. | | `quote.quoteId` | string | The quote's unique id. Read-only. | | `quote.isSubmitted` | boolean | Whether the quote has been finalised and sent (true) or is still a draft (false). | | `quote.customerId` | string | The id of the customer this quote belongs to. Always present. Read-only. | | `quote.customerCompanyName` | string | The customer's company name. Read-only. | | `quote.subtotal` | Money | Quote subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `quote.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.taxAmount` | Money | Total tax for the quote, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `quote.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.total` | Money | Quote grand total. Derived by the server; read-only. | | `quote.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.margin` | Money | Quote margin, computed by the server from the line items. Read-only. | | `quote.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.totalCost` | Money | Total cost of the quote, computed by the server from the line items. Read-only. | | `quote.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.discountAmount` | Money | The quote-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `quote.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.lines` | SalesLine[] | The quote's line items. | | `quote.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `quote.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `quote.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `quote.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `quote.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `quote.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `quote.lines.flashing.productName` | string | The display name of the flashing material. | | `quote.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `quote.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `quote.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `quote.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `quote.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `quote.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `quote.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `quote.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `quote.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `quote.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `quote.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `quote.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `quote.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `quote.adjustments` | Adjustment[] | Quote-level fees, discounts, and markups. At most one fee per quote is allowed. | | `quote.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `quote.adjustments.title` | string | A label describing the adjustment. | | `quote.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `quote.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `quote.fulfilmentMethod` | FulfilmentMethod | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `quote.billingAddress` | Address | Billing address for the quote. | | `quote.billingAddress.address1` | string | First address line (street number and name). | | `quote.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.billingAddress.city` | string | City or suburb. | | `quote.billingAddress.state` | string | State, province, or region. | | `quote.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `quote.deliveryAddress.address1` | string | First address line (street number and name). | | `quote.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.deliveryAddress.city` | string | City or suburb. | | `quote.deliveryAddress.state` | string | State, province, or region. | | `quote.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `quote.installAddress.address1` | string | First address line (street number and name). | | `quote.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.installAddress.city` | string | City or suburb. | | `quote.installAddress.state` | string | State, province, or region. | | `quote.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryFee` | Money | The delivery fee quoted on the order. Read-only. | | `quote.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.reference` | string | Free-text external reference, e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. | | `quote.orderNumber` | string | Factory's number for this quote. Assigned by the server when the quote is created, sequential within your company, and never changed — the quote keeps this number if it becomes an order. Shown as "Quote #" on the quote PDF and email, as "Order #" in the Factory app (order list, order page, workflow board) and on invoices and delivery documents, and used as the document reference in the accounting integrations. Read-only. | | `quote.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `quote.contact` | Contact | The quote's point-of-contact person. | | `quote.contact.name` | string | The contact person's name. | | `quote.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `quote.contact.phone` | string | The contact's landline phone number. | | `quote.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `quote.notes` | string | Free-text notes about the quote. | | `quote.requiredAt` | string | The date the customer needs the order by, if the quote is accepted. | | `quote.quotedAt` | string | The date the quote was issued. | | `quote.createdAt` | string | When the quote was created. Read-only. | | `quote.submittedAt` | string | When the quote was sent (finalised). Read-only. | | `quote.lastUpdatedAt` | string | When the quote was last updated. Read-only. | | `quote.createdByUserId` | string | The id of the user who created the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.submittedByUserId` | string | The id of the user who sent the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.tax` | TaxDetail | The tax descriptor for the quote: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `quote.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `quote.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `quote.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `quote.labels` | Label[] | The labels attached to this quote, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `quote.labels.labelId` | string | The label's id. | | `quote.labels.name` | string | Display name of the label. Unique within your company. | | `quote.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `quote.labourTotal` | Money | The total charged for labour on the quote: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `quote.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # DELETE /v1/quotes/{quoteId}/lines/{lineId} **Service:** Quotes **Operation:** `QuoteService_DeleteQuoteLineItem` Identify the line by the server-assigned id returned when you read the quote; the quote's totals are recomputed after removal. A line id that does not belong to the quote is rejected with HTTP 404 (not_found). A flashing line cannot yet be removed and is rejected with HTTP 501 (not_implemented). Concurrent writes to the same line are not detected: the last write wins. Re-read the quote after writing if other clients may be editing it. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote the line belongs to, as returned by CreateQuote. | | `lineId` | path | string | yes | The server-assigned id of the line to remove, as returned when the quote is read. This has a different form from the quote id. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `DeleteQuoteLineItemResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, the quote has been accepted and converted to an order — manage its lines with the OrderService line endpoints — or the line id does not identify a line on this quote. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `DeleteQuoteLineItemResponse` | Field | Type | Description | | --- | --- | --- | | `quote` | Quote | The updated quote, with the line removed and totals recomputed. | | `quote.quoteId` | string | The quote's unique id. Read-only. | | `quote.isSubmitted` | boolean | Whether the quote has been finalised and sent (true) or is still a draft (false). | | `quote.customerId` | string | The id of the customer this quote belongs to. Always present. Read-only. | | `quote.customerCompanyName` | string | The customer's company name. Read-only. | | `quote.subtotal` | Money | Quote subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `quote.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.taxAmount` | Money | Total tax for the quote, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `quote.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.total` | Money | Quote grand total. Derived by the server; read-only. | | `quote.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.margin` | Money | Quote margin, computed by the server from the line items. Read-only. | | `quote.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.totalCost` | Money | Total cost of the quote, computed by the server from the line items. Read-only. | | `quote.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.discountAmount` | Money | The quote-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `quote.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.lines` | SalesLine[] | The quote's line items. | | `quote.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `quote.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `quote.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `quote.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `quote.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `quote.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `quote.lines.flashing.productName` | string | The display name of the flashing material. | | `quote.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `quote.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `quote.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `quote.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quote.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `quote.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `quote.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `quote.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `quote.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `quote.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `quote.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `quote.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quote.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quote.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quote.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quote.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quote.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quote.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quote.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `quote.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quote.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `quote.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quote.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quote.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quote.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quote.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quote.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quote.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quote.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quote.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quote.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quote.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quote.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quote.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quote.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quote.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quote.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quote.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quote.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quote.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quote.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quote.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quote.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quote.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quote.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `quote.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quote.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quote.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quote.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quote.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quote.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quote.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quote.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `quote.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `quote.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `quote.adjustments` | Adjustment[] | Quote-level fees, discounts, and markups. At most one fee per quote is allowed. | | `quote.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `quote.adjustments.title` | string | A label describing the adjustment. | | `quote.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `quote.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `quote.fulfilmentMethod` | FulfilmentMethod | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `quote.billingAddress` | Address | Billing address for the quote. | | `quote.billingAddress.address1` | string | First address line (street number and name). | | `quote.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.billingAddress.city` | string | City or suburb. | | `quote.billingAddress.state` | string | State, province, or region. | | `quote.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `quote.deliveryAddress.address1` | string | First address line (street number and name). | | `quote.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.deliveryAddress.city` | string | City or suburb. | | `quote.deliveryAddress.state` | string | State, province, or region. | | `quote.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `quote.installAddress.address1` | string | First address line (street number and name). | | `quote.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `quote.installAddress.city` | string | City or suburb. | | `quote.installAddress.state` | string | State, province, or region. | | `quote.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quote.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quote.deliveryFee` | Money | The delivery fee quoted on the order. Read-only. | | `quote.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quote.reference` | string | Free-text external reference, e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. | | `quote.orderNumber` | string | Factory's number for this quote. Assigned by the server when the quote is created, sequential within your company, and never changed — the quote keeps this number if it becomes an order. Shown as "Quote #" on the quote PDF and email, as "Order #" in the Factory app (order list, order page, workflow board) and on invoices and delivery documents, and used as the document reference in the accounting integrations. Read-only. | | `quote.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `quote.contact` | Contact | The quote's point-of-contact person. | | `quote.contact.name` | string | The contact person's name. | | `quote.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `quote.contact.phone` | string | The contact's landline phone number. | | `quote.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `quote.notes` | string | Free-text notes about the quote. | | `quote.requiredAt` | string | The date the customer needs the order by, if the quote is accepted. | | `quote.quotedAt` | string | The date the quote was issued. | | `quote.createdAt` | string | When the quote was created. Read-only. | | `quote.submittedAt` | string | When the quote was sent (finalised). Read-only. | | `quote.lastUpdatedAt` | string | When the quote was last updated. Read-only. | | `quote.createdByUserId` | string | The id of the user who created the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.submittedByUserId` | string | The id of the user who sent the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quote.tax` | TaxDetail | The tax descriptor for the quote: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `quote.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `quote.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `quote.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `quote.labels` | Label[] | The labels attached to this quote, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `quote.labels.labelId` | string | The label's id. | | `quote.labels.name` | string | Display name of the label. Unique within your company. | | `quote.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `quote.labourTotal` | Money | The total charged for labour on the quote: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `quote.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quote.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # GET /v1/quotes/{quoteId}/messages **Service:** Quotes **Operation:** `QuoteService_ListMessages` Returns the quote's conversation — the same thread shown in the Factory app's Collaborate tab — oldest message first: text written by your team, files they attached, and documents the app generated. Each message carries its attachments, each with a presigned download URL. Most attachments are posted without any text, so expect many file-only messages. Signature captures collected in the app are not returned. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote whose conversation to list. Required. | | `pageSize` | query | integer | no | Maximum number of messages to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `quotesListMessagesResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, or the quote has been accepted and converted to an order — its conversation carries over: read it with the order-side endpoint, swapping the id's quote_ prefix for order_ (the 26-character suffix stays the same). | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `quotesListMessagesResponse` | Field | Type | Description | | --- | --- | --- | | `messages` | Message[] | The messages in this page, oldest first. | | `messages.id` | string | The id of this message. | | `messages.authorName` | string | The display name of the person who wrote the message. Empty when the author's account no longer exists. | | `messages.authorEmail` | string | The email address of the person who wrote the message. Empty when the author's account no longer exists. | | `messages.text` | string | The message text, as an HTML fragment. Messages written in the Factory app carry the app's markup (including @-mentions); plain text posted through this API is HTML-escaped and `

`-wrapped on write, and reads back in that stored form. Empty for file-only messages — most attachments are posted without any text. | | `messages.createdAt` | string | When the message was posted. | | `messages.attachments` | Attachment[] | The files attached to this message, if any. | | `messages.attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `messages.attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `messages.attachments.filename` | string | The file's name, as uploaded. | | `messages.attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `messages.attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `messages.attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `messages.attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `messages.attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `messages.attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/quotes/{quoteId}/messages **Service:** Quotes **Operation:** `QuoteService_PostMessage` Adds a plain-text message to the quote's conversation, visible immediately in the Factory app's Collaborate tab. The message is stored as an HTML fragment — the posted text HTML-escaped and `

`-wrapped — and reads back in that form. There is no way to @-mention a user through this API: because text is escaped, mention markup renders as literal text and notifies no one. PostMessage sends text on its own; to send a file with text, use the caption on UploadAttachment. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `quoteId` | path | string | yes | The id of the quote to post to. Required. | ## Request body `QuoteServicePostMessageBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `text` | string | yes | The message text, as plain text. Required. | | `requestId` | string | no | Optional idempotency key for this request: an opaque, client-generated UUID, scoped to the API key that sends it. Requests carrying the same key are executed at most once, so a retry can never create a second copy. Any repeat is rejected with HTTP 409: if the original request completed, the body's `resourceId` carries the id it created; if its outcome is still unknown, or the key is reused with a materially different body, verify with a read before retrying with a fresh key. Keys are retained for at least 24 hours. If the idempotency store is unavailable, requests carrying a key are rejected with HTTP 503 (requests without a key are unaffected). Omit the key for no idempotency guarantee. | | `fields` | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `quotesPostMessageResponse` | A successful response. | | 400 | `Error` | The request was rejected because it failed validation. The body is a validation-failure envelope: an overall type and message, the echoed `requestId`, and one entry per field-level problem (each with a stable code, a message, and a param pointing at the offending field). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No quote with the given id exists for the authenticated company, or the quote has been accepted and converted to an order — its conversation carries over: post to the order-side endpoint, swapping the id's quote_ prefix for order_ (the 26-character suffix stays the same). | | 409 | `Error` | The idempotency key in `requestId` conflicts with an earlier use: the original request already completed (`resourceId` in the error body carries the id of the message it posted), its outcome is still unknown, or the key was reused with a materially different body. Verify the conversation with ListMessages, then send a new `requestId` to post a new message. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `quotesPostMessageResponse` | Field | Type | Description | | --- | --- | --- | | `message` | Message | The created message. | | `message.id` | string | The id of this message. | | `message.authorName` | string | The display name of the person who wrote the message. Empty when the author's account no longer exists. | | `message.authorEmail` | string | The email address of the person who wrote the message. Empty when the author's account no longer exists. | | `message.text` | string | The message text, as an HTML fragment. Messages written in the Factory app carry the app's markup (including @-mentions); plain text posted through this API is HTML-escaped and `

`-wrapped on write, and reads back in that stored form. Empty for file-only messages — most attachments are posted without any text. | | `message.createdAt` | string | When the message was posted. | | `message.attachments` | Attachment[] | The files attached to this message, if any. | | `message.attachments.id` | string | The id of this attachment. Use it to retrieve or delete the attachment. | | `message.attachments.messageId` | string | The id of the conversation message this attachment belongs to. | | `message.attachments.filename` | string | The file's name, as uploaded. | | `message.attachments.contentType` | string | The file's media type (for example `image/png` or `application/pdf`). | | `message.attachments.sizeBytes` | string | The file's size in bytes, rounded to kilobyte precision. Files smaller than one kilobyte read as 0. | | `message.attachments.url` | string | A presigned URL to download the file. The URL expires; re-read the attachment for a fresh one rather than storing it. | | `message.attachments.thumbnailUrl` | string | A presigned URL to a small preview image, for image attachments. Empty when no preview exists. Expires like `url`. | | `message.attachments.isGenerated` | boolean | True when this file is a document the Factory app generated (for example a quote or invoice PDF) rather than a file somebody uploaded. Generated documents cannot be deleted through this API. | | `message.attachments.generatedType` | string | For generated documents, the kind of document (for example `quote` or `invoice`). Not a fixed list; new kinds may appear. Empty for uploaded files. | --- # GET /v1/quotes:query **Service:** Quotes **Operation:** `QuoteService_QueryQuotes` Like ListQuotes, but ordered by when each quote last changed (oldest first) and filterable by an `updatedSince` / `updatedBefore` time window — so a poller fetches only what changed since its last run and checkpoints on the most recent quote it saw. Use ListQuotes for simple lookups; use this to keep an external system (CRM, reporting) in sync. Paginated via `pageSize` / `pageToken`. Quotes have no workflow or payment status. Available filters: `customerId`, reference (substring), `isSubmitted` (draft/sent), `labelIds`, `fulfilmentMethod`, `createdAfter`/`createdBefore` (creation window), `requiredAfter`/`requiredBefore` (required-by window), and `includeArchived`. One migration to expect while syncing: a quote accepted in the Factory app becomes an order. It leaves this feed without a tombstone entry — there is no final "deleted" record — and reappears in QueryOrders as an order id. The two ids share their 26-character suffix and differ only in prefix: a quote "quote_" becomes the order "order_". Reconcile a quote that stops updating here by swapping the prefix and checking the order feed for that id. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `updatedSince` | query | string | no | Return only quotes updated at or after this time. Pass your last sync time here to fetch just what changed since then; omit to start from the beginning. This is the primary filter for incremental sync. | | `updatedBefore` | query | string | no | Optional upper bound: return only quotes updated strictly before this time. Combine with `updatedSince` to page through a bounded window; omit for "up to now". | | `pageSize` | query | integer | no | Maximum number of quotes to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `customerId` | query | string | no | Optional: only return quotes for this customer id. | | `reference` | query | string | no | Optional: only return quotes whose external reference contains this text, compared case-insensitively (a substring match, not exact-equals). | | `isSubmitted` | query | boolean | no | Optional: filter by whether the quote has been sent. Leave unset to return all quotes; set to false to return only drafts; set to true to return only sent quotes. | | `createdAfter` | query | string | no | Only quotes created at or after this time (inclusive). Optional; filters on the quote's creation time, independent of the updated window — combine the two windows to sync recent changes for quotes created in a period. Note: this parameter is named `createdAfter`, not `createdSince` — it is a range filter, not a sync checkpoint. | | `createdBefore` | query | string | no | Only quotes created strictly before this time (exclusive). Optional; combine with `createdAfter` to bound a creation window. | | `fulfilmentMethod` | query | string | no | Optional: only return quotes with this fulfilment method. Leave unset to include all. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `requiredAfter` | query | string | no | Only quotes required at or after this time. Optional; filters on the quote's required-by date, independent of the other filters. | | `requiredBefore` | query | string | no | Only quotes required strictly before this time. Optional; combine with `requiredAfter` to bound a required-by window. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `QueryQuotesResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `QueryQuotesResponse` | Field | Type | Description | | --- | --- | --- | | `quotes` | Quote[] | The quotes in this page, oldest change first. Use the last quote's update time as the `updatedSince` for your next call. | | `quotes.quoteId` | string | The quote's unique id. Read-only. | | `quotes.isSubmitted` | boolean | Whether the quote has been finalised and sent (true) or is still a draft (false). | | `quotes.customerId` | string | The id of the customer this quote belongs to. Always present. Read-only. | | `quotes.customerCompanyName` | string | The customer's company name. Read-only. | | `quotes.subtotal` | Money | Quote subtotal — the sum of line totals before tax. Derived by the server from the line items; read-only. | | `quotes.subtotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.subtotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.taxAmount` | Money | Total tax for the quote, as an absolute money amount (the tax `amount`, not a rate). Derived by the server from your account's tax settings; read-only. See `tax` for the rate, code, and jurisdiction that produced it. | | `quotes.taxAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.taxAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.total` | Money | Quote grand total. Derived by the server; read-only. | | `quotes.total.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.total.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.margin` | Money | Quote margin, computed by the server from the line items. Read-only. | | `quotes.margin.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.margin.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.totalCost` | Money | Total cost of the quote, computed by the server from the line items. Read-only. | | `quotes.totalCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.totalCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.discountAmount` | Money | The quote-level discount total, rolled up from the per-line discounts and the discount entries in `adjustments`. Read-only. | | `quotes.discountAmount.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.discountAmount.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.lines` | SalesLine[] | The quote's line items. | | `quotes.lines.onTheFly` | OnTheFlyLine | A free-form line not tied to a catalogue product. Set exactly one of the six line-type fields. | | `quotes.lines.catalogue` | CatalogueLine | A line for a product from your catalogue. Set exactly one of the six line-type fields. | | `quotes.lines.labour` | LabourLine | A line charging for labour. Set exactly one of the six line-type fields. | | `quotes.lines.notes` | NotesLine | A note line, with no pricing. Set exactly one of the six line-type fields. | | `quotes.lines.flashing` | FlashingLine | A flashing line, with its drawing and geometry. Set exactly one of the six line-type fields. A sheet-metal flashing line item: the flashing material, its specification, pricing, and its drawings. A flashing line is created with its document (CreateQuote, CreateOrder) or added whole to a quote (AddQuoteLineItem); it cannot yet be added to an existing order, updated, or removed individually — those calls answer HTTP 501 (not_implemented). Its specification (`bends`, `totalGirth`, `totalLength`, `subitems`) and its prices are stored exactly as you send them: the API never derives, prices, or cross-checks a flashing, so compute them yourself from the drawing and cut list as described on each field. Lengths and girths are always millimetres (metres for `totalLength`), whatever the account's measurement system. | | `quotes.lines.flashing.templateId` | string | The id of the flashing material or template. Required when the order or quote is submitted. | | `quotes.lines.flashing.productName` | string | The display name of the flashing material. | | `quotes.lines.flashing.priceLevel` | string | The name of the price level applied to this line (for example "A"), not a price level id. Required when the order or quote is submitted; an empty value is stored as "A". | | `quotes.lines.flashing.colour` | string | The flashing colour. Required when the order or quote is submitted, and validated against the colours available for the template when `templateId` is set; without a template it is stored unchecked. | | `quotes.lines.flashing.thickness` | string | The material thickness, as a decimal string in millimetres (for example "0.55"). Required when the order or quote is submitted. Send the thickness of the selected template (`templateId`) — each thickness is a distinct template. It is stored as sent and not compared with the template, so a mismatch is saved silently. | | `quotes.lines.flashing.bends` | string | The number of bends in the flashing, as a decimal string. Count one bend for each point that joins two edges, plus the bends each finish adds (crush fold 2, open hook 2, feather 1, drip edge 1; the account's `crushFoldBendCount` setting, when set, replaces the crush-fold count). With `otherSides`, take the count from the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quotes.lines.flashing.totalGirth` | string | The total girth of the flashing in millimetres, as a decimal string: the sum of every edge size in the drawing's `lines.values` (hidden edges excluded) plus the `size` of every finish. Edges of 110, 320 and 10 with a 10 mm crush fold give "450". With `otherSides`, use the side with the largest girth. Required when the order or quote is submitted, and must then be greater than zero. Stored as sent; not checked against the drawing. | | `quotes.lines.flashing.totalLength` | string | The total length of the flashing in metres, as a decimal string: the sum over `subitems` of amount × length ÷ 1000. Three pieces at 1200 plus two at 925 give "5.45". Stored as sent; not checked against `subitems`. | | `quotes.lines.flashing.unitPrice` | Money | The price per metre of the flashing. Required when the order or quote is submitted. | | `quotes.lines.flashing.totalPrice` | Money | The total price for this line. Stored as sent and never derived or checked. Factory prices a flashing as `unitPrice` × the priced length, where each piece shorter than the account's `minimumFlashingLengthMm` is charged at that minimum, rounded to the cent. | | `quotes.lines.flashing.customPrice` | Money | A custom total price for the line. When set, the document's subtotal uses it in place of `totalPrice`; `unitPrice` and `customPricePerLength` never enter the subtotal directly. | | `quotes.lines.flashing.customPricePerLength` | Money | A custom price per metre. Factory uses it in place of `unitPrice` when computing `totalPrice`; send the resulting `totalPrice` yourself, as the API does not recompute it. | | `quotes.lines.flashing.subitems` | object[] | The cut list: one entry per piece length, each an object with `amount` (the number of pieces) and `length` (the piece length in millimetres), both JSON numbers — for example `[{"amount": 3, "length": 1200}, {"amount": 2, "length": 925}]`. Stored as sent and not validated; a flashing with no lengths cannot be completed in Factory. Unlike `measurements` on other line kinds, lengths here are millimetre numbers, not metre strings. | | `quotes.lines.flashing.isTaxFree` | boolean | Whether this line is exempt from tax. Omit it and the line follows the customer (`disableTaxByDefault`); a value you send is kept as sent. In this version the flag is stored and read back but not applied: a flashing line is taxed at your account's rate whether or not it is set. | | `quotes.lines.flashing.drawing` | FlashingDrawing | The main drawing for the flashing: the folded profile whose edge sizes give `totalGirth` and `bends`. Required — a flashing line without a drawing is rejected with HTTP 400 (validation_failure). Its `side` defaults to the far side. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quotes.lines.flashing.drawing.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quotes.lines.flashing.drawing.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quotes.lines.flashing.drawing.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quotes.lines.flashing.drawing.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quotes.lines.flashing.drawing.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quotes.lines.flashing.drawing.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quotes.lines.flashing.drawing.isDeleted` | boolean | Whether the drawing has been deleted. | | `quotes.lines.flashing.drawing.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quotes.lines.flashing.drawing.points.*.id` | string | The unique id of this point within the drawing. | | `quotes.lines.flashing.drawing.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quotes.lines.flashing.drawing.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quotes.lines.flashing.drawing.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quotes.lines.flashing.drawing.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quotes.lines.flashing.drawing.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quotes.lines.flashing.drawing.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quotes.lines.flashing.drawing.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quotes.lines.flashing.drawing.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quotes.lines.flashing.drawing.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quotes.lines.flashing.drawing.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.drawing.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.drawing.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quotes.lines.flashing.drawing.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quotes.lines.flashing.drawing.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quotes.lines.flashing.drawing.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quotes.lines.flashing.drawing.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.drawing.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.drawing.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quotes.lines.flashing.drawing.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quotes.lines.flashing.drawing.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quotes.lines.flashing.drawing.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quotes.lines.flashing.drawing.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.drawing.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.drawing.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.drawing.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quotes.lines.flashing.drawing.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quotes.lines.flashing.drawing.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quotes.lines.flashing.drawing.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quotes.lines.flashing.drawing.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quotes.lines.flashing.drawing.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quotes.lines.flashing.drawing.annotations.*.text.indent` | integer | The indentation level of this row. | | `quotes.lines.flashing.drawing.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quotes.lines.flashing.drawing.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quotes.lines.flashing.drawing.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quotes.lines.flashing.drawing.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quotes.lines.flashing.drawing.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quotes.lines.flashing.drawing.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.drawing.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quotes.lines.flashing.otherSides` | FlashingDrawing[] | Additional side drawings of the same flashing, used when the profile tapers: Factory compares each side's edge sizes with the main drawing's, edge by edge, and counts every differing edge as a taper. Their `side` defaults to the near side. When present, `totalGirth` and `bends` come from whichever side has the largest girth. A flashing drawing: its identity, metadata, geometry, and the URL of its rendered SVG image. Carried only by flashing lines (`drawing` / `otherSides` on FlashingLine) — drawings describe a flashing's folded profile and are not a general-purpose image or attachment type. Geometry is sent in the same shape the drawing tool holds it: `points` are the vertices, joined by the edges listed in each point's `vectors` and `connect`; `lines.values` gives each edge's real size in millimetres; `angles.values` gives the angle at each vertex. Point coordinates only lay out the sketch — the sizes in `lines` are the dimensions. A drawing whose `points`, `lines` or `angles` exceeds about 15,000 characters of JSON is rejected with HTTP 400 (validation_failure). | | `quotes.lines.flashing.otherSides.drawingId` | string | Server-assigned unique id of the drawing. Read-only output. | | `quotes.lines.flashing.otherSides.tempId` | string | A client-assigned correlation id supplied when creating the drawing. The write that creates the drawing returns it paired with the assigned `drawingId` in the response's `drawings` mapping (CreateQuote, CreateOrder, AddQuoteLineItem) — use that to address UploadDrawingSvg. It must be unique across every drawing in the request, other sides included; a repeated id is rejected with HTTP 400 (validation_failure). Omit it and the server assigns unique ids for you. It is not stored, so reads do not return it. | | `quotes.lines.flashing.otherSides.drawingNumber` | integer | The drawing's display number within the order or quote. Not assigned by the server: it is stored as sent (0 when omitted), and reads and printed documents order drawings by it, so number them 0, 1, 2… in line order. | | `quotes.lines.flashing.otherSides.side` | DrawingSide | Which face of the flashing this drawing represents. When not specified, a line's main `drawing` is the far side and each of its `otherSides` is the near side. Which face of the flashing a drawing represents. When not specified, a flashing line's main `drawing` is the far side and each of its `otherSides` is the near side. - DRAWING_SIDE_UNSPECIFIED: Default, unset value. - DRAWING_SIDE_FAR: The far side of the flashing. - DRAWING_SIDE_NEAR: The near side of the flashing. One of: DRAWING_SIDE_UNSPECIFIED, DRAWING_SIDE_FAR, DRAWING_SIDE_NEAR. | | `quotes.lines.flashing.otherSides.isFreeDrawing` | boolean | Whether the edge sizes in `lines.values` are the drawing's dimensions. Send `true`: when false, Factory measures each edge from the points' canvas coordinates instead, so girth, bends and the displayed sizes come from the sketch rather than from the sizes you supplied. | | `quotes.lines.flashing.otherSides.isLargeBoxSize` | boolean | Whether the drawing uses the large box size. | | `quotes.lines.flashing.otherSides.isDeleted` | boolean | Whether the drawing has been deleted. | | `quotes.lines.flashing.otherSides.points` | map | The vertices of the drawing, keyed by point id. Record each edge at both ends: `p1.vectors` lists `p2` exactly when `p2.connect` lists `p1`. Required when the order or quote is submitted. A vertex in the drawing. Points are connected by edges to form the flashing profile. | | `quotes.lines.flashing.otherSides.points.*.id` | string | The unique id of this point within the drawing. | | `quotes.lines.flashing.otherSides.points.*.x` | number | The x coordinate of the point, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.y` | number | The y coordinate of the point, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.vectors` | string[] | The ids of points reached by edges leaving this point. Every edge must also appear in the target point's `connect`. | | `quotes.lines.flashing.otherSides.points.*.connect` | string[] | The ids of points connected to this point by incoming edges. Every edge must also appear in the source point's `vectors`. | | `quotes.lines.flashing.otherSides.points.*.finish` | Finish | Optional end treatment at this point. An end treatment applied to a point on a flashing edge (for example a hook or fold). Optional on a point. | | `quotes.lines.flashing.otherSides.points.*.finish.type` | FinishType | The kind of end treatment. The end-treatment applied to a flashing edge. Each type has a short code used in the finish's `label`, adds its `size` to the flashing's total girth, and counts a fixed number of bends. - FINISH_TYPE_UNSPECIFIED: Unspecified end treatment. - FINISH_TYPE_CRUSH_FOLD: A crush fold. Code "cf"; counts 2 bends (or the account's `crushFoldBendCount` when set). - FINISH_TYPE_OPEN_HOOK: An open hook. Code "oh"; counts 2 bends. - FINISH_TYPE_FEATHER: A feathered edge. Code "fe"; counts 1 bend. - FINISH_TYPE_DRIP_EDGE: A drip edge. Code "de"; counts 1 bend. One of: FINISH_TYPE_UNSPECIFIED, FINISH_TYPE_CRUSH_FOLD, FINISH_TYPE_OPEN_HOOK, FINISH_TYPE_FEATHER, FINISH_TYPE_DRIP_EDGE. | | `quotes.lines.flashing.otherSides.points.*.finish.size` | number | The size of the end treatment in millimetres. It counts toward the flashing's `totalGirth`. | | `quotes.lines.flashing.otherSides.points.*.finish.label` | string | The display label for this finish: the type's code followed by the size, e.g. "cf10" for a 10 mm crush fold or "oh25" for a 25 mm open hook. | | `quotes.lines.flashing.otherSides.points.*.finish.flip` | boolean | Which side of the edge the finish folds to. Looking from the finished end along its edge (screen coordinates, y pointing down), `false` puts the fold on the right-hand side and `true` on the left. A feathered edge is drawn mirrored relative to the other types. | | `quotes.lines.flashing.otherSides.points.*.finish.position` | LabelPosition | Optional bounding box for this finish's label. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.otherSides.points.*.finish.position.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.finish.position.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.finish.position.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.points.*.finish.position.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines` | LineSet | The edge sizes between points in millimetres, with their optional label positions. Give every edge a size — Factory counts the girth of a drawing with a missing size as 0. When omitted, the drawing is stored with an empty set and reads return `lines` with empty `values` and `positions`. The collection of edge lengths and their label positions for a drawing. | | `quotes.lines.flashing.otherSides.lines.values` | map | The size entry for each edge, keyed by the ids of the two points it connects joined with a hyphen, source first: the edge from `p1` to `p2` is `"p1-p2"`. The length entry for a single edge between two points. | | `quotes.lines.flashing.otherSides.lines.values.*.size` | number | The size of the edge in millimetres. Omitted when a size has not yet been entered — but give every edge a size, or Factory counts the girth as 0. | | `quotes.lines.flashing.otherSides.lines.values.*.hidden` | boolean | Whether this edge's length label is hidden. | | `quotes.lines.flashing.otherSides.lines.positions` | map | The label box for each edge's size label, keyed like `values`. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.otherSides.lines.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.lines.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles` | AngleSet | The vertex angles in degrees, with their optional label positions. When omitted, the drawing is stored with an empty set and reads return `angles` with empty `values` and `positions`. The collection of vertex angles and their label positions for a drawing. | | `quotes.lines.flashing.otherSides.angles.values` | map | The angle entry for each vertex, keyed by point id. The angle entry for a single point (vertex) in the drawing. | | `quotes.lines.flashing.otherSides.angles.values.*.angle` | number | The angle at this vertex, in degrees. Omitted when no angle is set. | | `quotes.lines.flashing.otherSides.angles.values.*.hidden` | boolean | Whether this vertex's angle label is hidden. Omitted when not set. | | `quotes.lines.flashing.otherSides.angles.positions` | map | The label box for each vertex's angle label, keyed by point id. Optional: Factory places any label without a box itself. A label's bounding box in canvas (SVG) coordinate space. Used for line labels, angle labels, and finish labels. | | `quotes.lines.flashing.otherSides.angles.positions.*.x` | number | The x coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles.positions.*.y` | number | The y coordinate of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles.positions.*.width` | number | The width of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.angles.positions.*.height` | number | The height of the label box, in canvas space. | | `quotes.lines.flashing.otherSides.annotations` | map | Free-text annotations on the drawing, keyed by annotation id. A free-text annotation placed on the drawing. | | `quotes.lines.flashing.otherSides.annotations.*.x` | number | The x coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.annotations.*.y` | number | The y coordinate of the annotation, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.annotations.*.width` | number | The width of the annotation box, in canvas space. | | `quotes.lines.flashing.otherSides.annotations.*.height` | number | The height of the annotation box, in canvas space. | | `quotes.lines.flashing.otherSides.annotations.*.value` | string | The annotation text as a flat string. May be present alongside the structured `text` form. | | `quotes.lines.flashing.otherSides.annotations.*.text` | TextRow[] | The annotation text in structured rows. May be present alongside `value`. One row of text within a text annotation. | | `quotes.lines.flashing.otherSides.annotations.*.text.id` | integer | The 0-based index of this row within the text annotation. | | `quotes.lines.flashing.otherSides.annotations.*.text.indent` | integer | The indentation level of this row. | | `quotes.lines.flashing.otherSides.annotations.*.text.value` | string[] | The text segments that make up this row, in order. | | `quotes.lines.flashing.otherSides.annotations.*.rotateDeg` | number | Optional rotation of the annotation, in degrees. | | `quotes.lines.flashing.otherSides.frontArrow` | Arrow | The arrow marking the front face of the flashing. Optional. The arrow that indicates the front-facing direction of the flashing. | | `quotes.lines.flashing.otherSides.frontArrow.x` | number | The x coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.frontArrow.y` | number | The y coordinate of the arrow, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.frontArrow.angle` | number | The direction the arrow points, in degrees, turning clockwise on screen from 0 = left: 90 points up, 180 right, 270 down. | | `quotes.lines.flashing.otherSides.squareAngle` | SquareAngle | The right-angle (90°) marker placed on the drawing. | | `quotes.lines.flashing.otherSides.squareAngle.x` | number | The x coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.squareAngle.y` | number | The y coordinate of the marker, in canvas space. Omitted until placed. | | `quotes.lines.flashing.otherSides.svgUrl` | string | A presigned URL to the rendered SVG image of the drawing. Read-only output; populated after the SVG is uploaded via UploadDrawingSvg on OrderService or QuoteService. | | `quotes.lines.flashing.tax` | TaxDetail | The tax applied to this line: rate, code, and jurisdiction. Complements the `isTaxFree` flag. In v1 the server applies your account's tax settings to taxable lines; this object reserves the shape for richer per-line tax later. | | `quotes.lines.productKit` | ProductKitLine | A line for a predefined product kit. Set exactly one of the six line-type fields. | | `quotes.lines.id` | string | The server-assigned id of this line — unique and stable for the life of the line. Returned when a quote is read; pass it to update or remove the line. Not set when creating a line (the server assigns it). This id has a different form from the quote id. | | `quotes.adjustments` | Adjustment[] | Quote-level fees, discounts, and markups. At most one fee per quote is allowed. | | `quotes.adjustments.type` | AdjustmentType | The kind of adjustment: a fee, discount, or markup. Required. The kind of order- or quote-level adjustment: a fee, a discount, or a markup. Only fees and discounts change the document's totals. - ADJUSTMENT_TYPE_UNSPECIFIED: Default, unset value. Not a valid choice when setting an adjustment. - ADJUSTMENT_TYPE_FEE: A fee added to the order or quote after any discounts, and taxed at the account's rate. It must be a fixed `amount`, not a `percent`, and at most one fee is allowed per order or quote. - ADJUSTMENT_TYPE_DISCOUNT: A discount subtracted from the lines' subtotal before tax and before any fee. A `percent` discount is taken of the lines' subtotal as it stands when the discount is applied; with several discounts, each applies to the subtotal left by the earlier ones, in the order they were added. - ADJUSTMENT_TYPE_MARKUP: A markup recorded against the order or quote. It is stored and returned on reads but changes no total: to mark up a document, raise its line prices. Any number of markups may be recorded. One of: ADJUSTMENT_TYPE_UNSPECIFIED, ADJUSTMENT_TYPE_FEE, ADJUSTMENT_TYPE_DISCOUNT, ADJUSTMENT_TYPE_MARKUP. | | `quotes.adjustments.title` | string | A label describing the adjustment. | | `quotes.adjustments.amount` | Money | The adjustment as a fixed money amount. Exactly one of `amount` or `percent` must be set. A fee must use this form. | | `quotes.adjustments.percent` | string | The adjustment as a percentage (for example, "10.00"). Exactly one of `amount` or `percent` must be set. For a discount, the percentage is taken of the lines' subtotal (see AdjustmentType); a fee given as a percent is rejected with HTTP 400 (validation_failure). | | `quotes.fulfilmentMethod` | FulfilmentMethod | How the customer would receive the order if the quote is accepted: pickup, delivery, or installation. How the customer receives the goods on an order or quote. Determines which address is used: delivery uses the delivery address, installation uses the install address. - FULFILMENT_METHOD_UNSPECIFIED: Default, unset value. - FULFILMENT_METHOD_PICKUP: The customer collects the goods themselves. - FULFILMENT_METHOD_DELIVERY: The goods are delivered to the delivery address. - FULFILMENT_METHOD_INSTALL: The goods are installed at the install address. One of: FULFILMENT_METHOD_UNSPECIFIED, FULFILMENT_METHOD_PICKUP, FULFILMENT_METHOD_DELIVERY, FULFILMENT_METHOD_INSTALL. | | `quotes.billingAddress` | Address | Billing address for the quote. | | `quotes.billingAddress.address1` | string | First address line (street number and name). | | `quotes.billingAddress.address2` | string | Second address line (unit, suite, or similar). | | `quotes.billingAddress.city` | string | City or suburb. | | `quotes.billingAddress.state` | string | State, province, or region. | | `quotes.billingAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quotes.billingAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quotes.deliveryAddress` | Address | Delivery address. Applies when `fulfilmentMethod` is DELIVERY. | | `quotes.deliveryAddress.address1` | string | First address line (street number and name). | | `quotes.deliveryAddress.address2` | string | Second address line (unit, suite, or similar). | | `quotes.deliveryAddress.city` | string | City or suburb. | | `quotes.deliveryAddress.state` | string | State, province, or region. | | `quotes.deliveryAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quotes.deliveryAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quotes.installAddress` | Address | Installation address. Applies when `fulfilmentMethod` is INSTALL. | | `quotes.installAddress.address1` | string | First address line (street number and name). | | `quotes.installAddress.address2` | string | Second address line (unit, suite, or similar). | | `quotes.installAddress.city` | string | City or suburb. | | `quotes.installAddress.state` | string | State, province, or region. | | `quotes.installAddress.postalCode` | string | Postal code (ZIP code, postcode). | | `quotes.installAddress.countryCode` | string | Country, as an uppercase ISO 3166-1 alpha-2 code (e.g. "AU"). Optional; when supplied, any other form is rejected. | | `quotes.deliveryFee` | Money | The delivery fee quoted on the order. Read-only. | | `quotes.deliveryFee.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.deliveryFee.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `quotes.reference` | string | Free-text external reference, e.g. the quote id or purchase order (PO) number from your own system. Shown as "PO #" in the Factory app and as "PO" on the quote and order documents. | | `quotes.orderNumber` | string | Factory's number for this quote. Assigned by the server when the quote is created, sequential within your company, and never changed — the quote keeps this number if it becomes an order. Shown as "Quote #" on the quote PDF and email, as "Order #" in the Factory app (order list, order page, workflow board) and on invoices and delivery documents, and used as the document reference in the accounting integrations. Read-only. | | `quotes.customFields` | object | The document's custom-field values, keyed by each field's configured key. | | `quotes.contact` | Contact | The quote's point-of-contact person. | | `quotes.contact.name` | string | The contact person's name. | | `quotes.contact.email` | string | The contact's email address. Optional; when provided, it must be a valid email address. | | `quotes.contact.phone` | string | The contact's landline phone number. | | `quotes.contact.mobile` | string | The contact's mobile number, normalized to E.164 format (e.g. +61400000000). | | `quotes.notes` | string | Free-text notes about the quote. | | `quotes.requiredAt` | string | The date the customer needs the order by, if the quote is accepted. | | `quotes.quotedAt` | string | The date the quote was issued. | | `quotes.createdAt` | string | When the quote was created. Read-only. | | `quotes.submittedAt` | string | When the quote was sent (finalised). Read-only. | | `quotes.lastUpdatedAt` | string | When the quote was last updated. Read-only. | | `quotes.createdByUserId` | string | The id of the user who created the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quotes.submittedByUserId` | string | The id of the user who sent the quote, empty when unset. Resolve the user via the Users API (which can return users who have since left). Read-only. | | `quotes.tax` | TaxDetail | The tax descriptor for the quote: rate, code, and jurisdiction. Not populated in this version — it reads back empty; `taxAmount` carries the tax actually applied at your account's rate. Read-only. | | `quotes.tax.rate` | string | The tax rate applied, as a decimal fraction (e.g. "0.10" means 10%). | | `quotes.tax.code` | string | The tax code, e.g. "GST", "VAT", or "SALES_TAX". Open-ended — not a fixed set. | | `quotes.tax.jurisdiction` | string | The tax jurisdiction, e.g. "AU", "GB", or "US-CA-LOS_ANGELES". Open-ended. | | `quotes.labels` | Label[] | The labels attached to this quote, oldest attachment first. Present on create responses when `labelIds` are supplied; absent from line-write responses. | | `quotes.labels.labelId` | string | The label's id. | | `quotes.labels.name` | string | Display name of the label. Unique within your company. | | `quotes.labels.colour` | string | Display colour of the label, as a hex string (for example "#FF69B4"). | | `quotes.labourTotal` | Money | The total charged for labour on the quote: the sum of the labour line totals, including labour components inside product kits. Derived by the server from the line items on every write; read-only. | | `quotes.labourTotal.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `quotes.labourTotal.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/suppliers **Service:** Suppliers **Operation:** `SupplierService_ListSuppliers` Returns a paginated list of your company's suppliers. Useful for discovering supplier ids to pass as filters on the inventory list. Suppliers are ordered alphabetically by name. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of suppliers to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `name` | query | string | no | Optional: only return suppliers whose name contains this text (case-insensitive substring match). | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListSuppliersResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListSuppliersResponse` | Field | Type | Description | | --- | --- | --- | | `suppliers` | Supplier[] | The suppliers in this page. | | `suppliers.supplierId` | string | The supplier's unique id. | | `suppliers.name` | string | The supplier's name. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/suppliers/{supplierId} **Service:** Suppliers **Operation:** `SupplierService_GetSupplier` Returns the supplier matching the given id. Useful when you already have a supplier id (for example from an inventory filter) and need its details without listing. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `supplierId` | path | string | yes | The id of the supplier to retrieve. Required. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetSupplierResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No supplier with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetSupplierResponse` | Field | Type | Description | | --- | --- | --- | | `supplier` | Supplier | The requested supplier. | | `supplier.supplierId` | string | The supplier's unique id. | | `supplier.name` | string | The supplier's name. | --- # GET /v1/users **Service:** Users **Operation:** `UserService_ListUsers` A company-scoped list of users, returning active users by default. Use it to find a user to assign to a labour line on an order. Users are returned in a stable order, sorted by username (the login name). User emails are populated only when the caller is a company administrator; other callers receive users without email addresses. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | Maximum number of users to return per page. 0 uses the server default; the server may cap the value. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `activeOnly` | query | boolean | no | Whether to return only active, non-deleted users. When omitted, defaults to true; set it explicitly to false to include inactive users. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListUsersResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or an invalid filter value. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListUsersResponse` | Field | Type | Description | | --- | --- | --- | | `users` | User[] | The users in this page. | | `users.userId` | string | The user's unique id. This is the value to use as a labour line's user id when assigning the user to a labour line on an order. | | `users.firstName` | string | The user's first name. | | `users.lastName` | string | The user's last name. | | `users.email` | string | The user's email address. In ListUsers responses this is populated only when the caller is a company administrator; GetUser always returns it. | | `users.isActive` | boolean | Whether the user is active. Only active users can be assigned to a labour line. | | `users.hourlyRateCharged` | Money | The default per-hour rate charged for this user's labour — what an hour of their work sells for. Zero when no rate has been set for the user. | | `users.hourlyRateCharged.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `users.hourlyRateCharged.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `users.hourlyRateCost` | Money | The per-hour cost of this user's labour to your company. Zero when no rate has been set for the user. | | `users.hourlyRateCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `users.hourlyRateCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/users/{userId} **Service:** Users **Operation:** `UserService_GetUser` Returns the user record — name, email, and active status — for the given id, scoped to your company. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `userId` | path | string | yes | The id of the user to retrieve. Required. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetUserResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a badly formed id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The authenticated user is not a company administrator. Retrieving a user by id is an administrator-only operation. | | 404 | `Error` | No user with the given id exists in the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetUserResponse` | Field | Type | Description | | --- | --- | --- | | `user` | User | The requested user. | | `user.userId` | string | The user's unique id. This is the value to use as a labour line's user id when assigning the user to a labour line on an order. | | `user.firstName` | string | The user's first name. | | `user.lastName` | string | The user's last name. | | `user.email` | string | The user's email address. In ListUsers responses this is populated only when the caller is a company administrator; GetUser always returns it. | | `user.isActive` | boolean | Whether the user is active. Only active users can be assigned to a labour line. | | `user.hourlyRateCharged` | Money | The default per-hour rate charged for this user's labour — what an hour of their work sells for. Zero when no rate has been set for the user. | | `user.hourlyRateCharged.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `user.hourlyRateCharged.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | | `user.hourlyRateCost` | Money | The per-hour cost of this user's labour to your company. Zero when no rate has been set for the user. | | `user.hourlyRateCost.amountMicros` | string | Amount in micros — millionths of the currency's major unit. 1.00 = 1_000_000 micros, so $12.34 is 12_340_000 — sent and returned over JSON as the string "12340000" (64-bit integers serialize as JSON strings). A whole number of cents (or pence, etc.) is a multiple of 10_000 micros. | | `user.hourlyRateCost.currency` | string | ISO 4217 currency code (3 letters, e.g. "AUD"). | --- # GET /v1/webhook-deliveries **Service:** Webhooks **Operation:** `WebhookService_ListWebhookDeliveries` Records cover the last 30 days. Filter by subscription, state, or a creation-time window; combine filters freely. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `webhookId` | query | string | no | Only deliveries for this subscription. Optional. | | `state` | query | string | no | Only deliveries in this state. Optional. One of: DELIVERY_STATE_UNSPECIFIED, DELIVERY_STATE_PENDING, DELIVERY_STATE_SUCCEEDED, DELIVERY_STATE_EXHAUSTED. | | `createdAfter` | query | string | no | Only deliveries created at or after this instant. Optional. | | `createdBefore` | query | string | no | Only deliveries created before this instant. Optional. | | `pageSize` | query | integer | no | The maximum number of records to return in one page. Optional; a server default applies when unset. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListWebhookDeliveriesResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token or a filter id with the wrong prefix. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure, a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListWebhookDeliveriesResponse` | Field | Type | Description | | --- | --- | --- | | `deliveries` | WebhookDelivery[] | One page of delivery records, newest first. | | `deliveries.deliveryId` | string | The delivery record's id. Read-only. | | `deliveries.webhookId` | string | The subscription this delivery targets. Read-only. | | `deliveries.eventId` | string | The event being delivered. Matches the `id` of the delivered payload — identical across retries and duplicates; deduplicate on it. Read-only. | | `deliveries.eventType` | string | The event's type (see `eventTypes` on Webhook for the taxonomy). Read-only. | | `deliveries.state` | DeliveryState | Where the delivery is in its lifecycle. Read-only. The state of one delivery (one event to one subscription). - DELIVERY_STATE_PENDING: Waiting for its first or next attempt (see `nextAttemptAt`). - DELIVERY_STATE_SUCCEEDED: The endpoint acknowledged the delivery with a 2xx response. - DELIVERY_STATE_EXHAUSTED: All retry attempts were used without a 2xx (about 10.6 hours across 6 attempts). Exhausted deliveries are not retried again. One of: DELIVERY_STATE_UNSPECIFIED, DELIVERY_STATE_PENDING, DELIVERY_STATE_SUCCEEDED, DELIVERY_STATE_EXHAUSTED. | | `deliveries.attemptCount` | integer | Attempts made so far (0 while pending its first attempt). Read-only. | | `deliveries.nextAttemptAt` | string | When the next attempt is due; unset once succeeded or exhausted. Read-only. | | `deliveries.lastAttemptedAt` | string | When the most recent attempt ran; unset before the first attempt. Read-only. | | `deliveries.lastStatusCode` | integer | The HTTP status the endpoint returned on the most recent attempt; 0 when the attempt never got a response (timeout, connection failure, a URL refused at send time, or the original event no longer being available). Read-only. | | `deliveries.lastResponseSnippet` | string | The start of the endpoint's response body on the most recent attempt, truncated to 1000 characters. Read-only. | | `deliveries.createdAt` | string | When the delivery record was created (the event's intake time). Read-only. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # GET /v1/webhook-deliveries/{deliveryId} **Service:** Webhooks **Operation:** `WebhookService_GetWebhookDelivery` Fetch one delivery record. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `deliveryId` | path | string | yes | The delivery record to fetch. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetWebhookDeliveryResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a delivery id that is not a whdel_ id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No delivery record with the given id exists for the authenticated company — including records past their 30-day retention. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure, a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetWebhookDeliveryResponse` | Field | Type | Description | | --- | --- | --- | | `delivery` | WebhookDelivery | The requested delivery record. | | `delivery.deliveryId` | string | The delivery record's id. Read-only. | | `delivery.webhookId` | string | The subscription this delivery targets. Read-only. | | `delivery.eventId` | string | The event being delivered. Matches the `id` of the delivered payload — identical across retries and duplicates; deduplicate on it. Read-only. | | `delivery.eventType` | string | The event's type (see `eventTypes` on Webhook for the taxonomy). Read-only. | | `delivery.state` | DeliveryState | Where the delivery is in its lifecycle. Read-only. The state of one delivery (one event to one subscription). - DELIVERY_STATE_PENDING: Waiting for its first or next attempt (see `nextAttemptAt`). - DELIVERY_STATE_SUCCEEDED: The endpoint acknowledged the delivery with a 2xx response. - DELIVERY_STATE_EXHAUSTED: All retry attempts were used without a 2xx (about 10.6 hours across 6 attempts). Exhausted deliveries are not retried again. One of: DELIVERY_STATE_UNSPECIFIED, DELIVERY_STATE_PENDING, DELIVERY_STATE_SUCCEEDED, DELIVERY_STATE_EXHAUSTED. | | `delivery.attemptCount` | integer | Attempts made so far (0 while pending its first attempt). Read-only. | | `delivery.nextAttemptAt` | string | When the next attempt is due; unset once succeeded or exhausted. Read-only. | | `delivery.lastAttemptedAt` | string | When the most recent attempt ran; unset before the first attempt. Read-only. | | `delivery.lastStatusCode` | integer | The HTTP status the endpoint returned on the most recent attempt; 0 when the attempt never got a response (timeout, connection failure, a URL refused at send time, or the original event no longer being available). Read-only. | | `delivery.lastResponseSnippet` | string | The start of the endpoint's response body on the most recent attempt, truncated to 1000 characters. Read-only. | | `delivery.createdAt` | string | When the delivery record was created (the event's intake time). Read-only. | --- # GET /v1/webhooks **Service:** Webhooks **Operation:** `WebhookService_ListWebhooks` Sorted by creation time, newest first. Secrets are never included. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `pageSize` | query | integer | no | The maximum number of subscriptions to return in one page. Optional; a server default applies when unset. | | `pageToken` | query | string | no | Opaque page token from a previous response, used to fetch the next page. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `ListWebhooksResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example an unusable page token. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure, a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `ListWebhooksResponse` | Field | Type | Description | | --- | --- | --- | | `webhooks` | Webhook[] | One page of subscriptions, newest first. Secrets are never included. | | `webhooks.webhookId` | string | The subscription's id. Read-only. | | `webhooks.url` | string | The HTTPS endpoint deliveries are POSTed to. Plain http URLs and endpoints resolving to private address space are rejected at create and update time. | | `webhooks.eventTypes` | string[] | The event types this subscription receives. At least one. Valid values are the v1 taxonomy: `order.created`, `order.updated`, `order.status_changed`, `order.archived`, `quote.created`, `quote.updated`, `quote.status_changed`, `quote.converted_to_order`. Unknown values are rejected naming the offender. | | `webhooks.status` | WebhookStatus | Whether the subscription is receiving deliveries. New subscriptions are enabled; see WebhookStatus for how a subscription becomes disabled. The lifecycle state of a webhook subscription. - WEBHOOK_STATUS_ENABLED: The subscription receives deliveries. - WEBHOOK_STATUS_DISABLED: The subscription receives no deliveries. Set manually via UpdateWebhook, or automatically when deliveries exhaust their retries and nothing has been delivered successfully for 7 days (auto-disable; re-enable via UpdateWebhook once the endpoint is healthy). One of: WEBHOOK_STATUS_UNSPECIFIED, WEBHOOK_STATUS_ENABLED, WEBHOOK_STATUS_DISABLED. | | `webhooks.description` | string | A free-form label for your own bookkeeping. | | `webhooks.secret` | string | The signing secret, in the form `whsec_<43 base64url characters>`. Returned ONLY in the CreateWebhook response — store it then; it cannot be retrieved again (rotation is not available in v1). The FULL string, prefix included, is the HMAC-SHA256 key for verifying the X-Factory-Signature header on deliveries. | | `webhooks.createdAt` | string | When the subscription was created. Read-only. | | `webhooks.updatedAt` | string | When the subscription was last changed. Read-only. | | `nextPageToken` | string | Token to pass as `pageToken` to fetch the next page; empty when there are no more results. | --- # POST /v1/webhooks **Service:** Webhooks **Operation:** `WebhookService_CreateWebhook` Creates a subscription delivering the requested event types to an HTTPS endpoint. The response is the only place the signing secret ever appears — store it before returning; it cannot be retrieved again. ## Request body `CreateWebhookRequest` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | The HTTPS endpoint to deliver events to. Required. Plain http URLs and endpoints resolving to private address space are rejected. | | `eventTypes` | string[] | no | The event types to deliver. Required, at least one; see `eventTypes` on Webhook for the valid values. Unknown values are rejected naming the offender. | | `description` | string | no | A free-form label for your own bookkeeping. Optional. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `CreateWebhookResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a non-HTTPS url, an endpoint resolving to private address space, an unknown event type (the message names it), or no event types at all. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure (for example "rate_limited" or "internal"), a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `CreateWebhookResponse` | Field | Type | Description | | --- | --- | --- | | `webhook` | Webhook | The created subscription. This response is the ONLY place secret is ever populated — store it now; it cannot be retrieved again. | | `webhook.webhookId` | string | The subscription's id. Read-only. | | `webhook.url` | string | The HTTPS endpoint deliveries are POSTed to. Plain http URLs and endpoints resolving to private address space are rejected at create and update time. | | `webhook.eventTypes` | string[] | The event types this subscription receives. At least one. Valid values are the v1 taxonomy: `order.created`, `order.updated`, `order.status_changed`, `order.archived`, `quote.created`, `quote.updated`, `quote.status_changed`, `quote.converted_to_order`. Unknown values are rejected naming the offender. | | `webhook.status` | WebhookStatus | Whether the subscription is receiving deliveries. New subscriptions are enabled; see WebhookStatus for how a subscription becomes disabled. The lifecycle state of a webhook subscription. - WEBHOOK_STATUS_ENABLED: The subscription receives deliveries. - WEBHOOK_STATUS_DISABLED: The subscription receives no deliveries. Set manually via UpdateWebhook, or automatically when deliveries exhaust their retries and nothing has been delivered successfully for 7 days (auto-disable; re-enable via UpdateWebhook once the endpoint is healthy). One of: WEBHOOK_STATUS_UNSPECIFIED, WEBHOOK_STATUS_ENABLED, WEBHOOK_STATUS_DISABLED. | | `webhook.description` | string | A free-form label for your own bookkeeping. | | `webhook.secret` | string | The signing secret, in the form `whsec_<43 base64url characters>`. Returned ONLY in the CreateWebhook response — store it then; it cannot be retrieved again (rotation is not available in v1). The FULL string, prefix included, is the HMAC-SHA256 key for verifying the X-Factory-Signature header on deliveries. | | `webhook.createdAt` | string | When the subscription was created. Read-only. | | `webhook.updatedAt` | string | When the subscription was last changed. Read-only. | --- # GET /v1/webhooks/{webhookId} **Service:** Webhooks **Operation:** `WebhookService_GetWebhook` The secret is never included — it is returned only by CreateWebhook. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `webhookId` | path | string | yes | The subscription to fetch. | | `fields` | query | string | no | Comma-separated response fields to include, using camelCase JSON names (e.g. `orderId,total.amountMicros`). Dot paths reach into nested objects and map transparently across arrays. Paths are relative to the resource, not the response envelope; envelope keys like `nextPageToken` are always preserved. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Unknown names are silently ignored. Takes precedence over `excludeFields` when both are provided. | | `excludeFields` | query | string | no | Comma-separated response fields to exclude, using camelCase JSON names. Dot paths and array-transparency work the same as `fields`. Paths are relative to the resource, not the response envelope. Only 2xx JSON responses are filtered; error bodies pass through unmodified. Ignored when `fields` is also provided. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `GetWebhookResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a webhook id that is not a whsub_ id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No webhook subscription with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure, a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `GetWebhookResponse` | Field | Type | Description | | --- | --- | --- | | `webhook` | Webhook | The requested subscription. secret is never populated here. | | `webhook.webhookId` | string | The subscription's id. Read-only. | | `webhook.url` | string | The HTTPS endpoint deliveries are POSTed to. Plain http URLs and endpoints resolving to private address space are rejected at create and update time. | | `webhook.eventTypes` | string[] | The event types this subscription receives. At least one. Valid values are the v1 taxonomy: `order.created`, `order.updated`, `order.status_changed`, `order.archived`, `quote.created`, `quote.updated`, `quote.status_changed`, `quote.converted_to_order`. Unknown values are rejected naming the offender. | | `webhook.status` | WebhookStatus | Whether the subscription is receiving deliveries. New subscriptions are enabled; see WebhookStatus for how a subscription becomes disabled. The lifecycle state of a webhook subscription. - WEBHOOK_STATUS_ENABLED: The subscription receives deliveries. - WEBHOOK_STATUS_DISABLED: The subscription receives no deliveries. Set manually via UpdateWebhook, or automatically when deliveries exhaust their retries and nothing has been delivered successfully for 7 days (auto-disable; re-enable via UpdateWebhook once the endpoint is healthy). One of: WEBHOOK_STATUS_UNSPECIFIED, WEBHOOK_STATUS_ENABLED, WEBHOOK_STATUS_DISABLED. | | `webhook.description` | string | A free-form label for your own bookkeeping. | | `webhook.secret` | string | The signing secret, in the form `whsec_<43 base64url characters>`. Returned ONLY in the CreateWebhook response — store it then; it cannot be retrieved again (rotation is not available in v1). The FULL string, prefix included, is the HMAC-SHA256 key for verifying the X-Factory-Signature header on deliveries. | | `webhook.createdAt` | string | When the subscription was created. Read-only. | | `webhook.updatedAt` | string | When the subscription was last changed. Read-only. | --- # DELETE /v1/webhooks/{webhookId} **Service:** Webhooks **Operation:** `WebhookService_DeleteWebhook` Deliveries stop immediately. Existing delivery records remain readable until their 30-day retention lapses. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `webhookId` | path | string | yes | The subscription to delete. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `DeleteWebhookResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a webhook id that is not a whsub_ id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No webhook subscription with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure, a human-readable message, and the request's idempotency key echoed back when one was supplied. | --- # PATCH /v1/webhooks/{webhookId} **Service:** Webhooks **Operation:** `WebhookService_UpdateWebhook` Fields left unset keep their current value. The secret cannot be changed (rotation is not available in v1); to stop deliveries set status to disabled, to retire an endpoint delete the subscription and create a new one. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `webhookId` | path | string | yes | The subscription to change. | ## Request body `UpdateWebhookBody` (application/json) | Field | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | A new HTTPS endpoint. Same validation as at create. | | `eventTypes` | string[] | no | A replacement event-type list (full replacement, not a merge). At least one when provided. | | `description` | string | no | A new description. | | `status` | WebhookStatus | no | Enable or disable deliveries. Re-enabling an auto-disabled subscription resumes deliveries of NEW events; exhausted deliveries are not revived. The lifecycle state of a webhook subscription. - WEBHOOK_STATUS_ENABLED: The subscription receives deliveries. - WEBHOOK_STATUS_DISABLED: The subscription receives no deliveries. Set manually via UpdateWebhook, or automatically when deliveries exhaust their retries and nothing has been delivered successfully for 7 days (auto-disable; re-enable via UpdateWebhook once the endpoint is healthy). One of: WEBHOOK_STATUS_UNSPECIFIED, WEBHOOK_STATUS_ENABLED, WEBHOOK_STATUS_DISABLED. | ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `UpdateWebhookResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a non-HTTPS url, an endpoint resolving to private address space, an unknown event type (the message names it), or an empty event-type list. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No webhook subscription with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure, a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `UpdateWebhookResponse` | Field | Type | Description | | --- | --- | --- | | `webhook` | Webhook | The subscription after the change. secret is never populated here. | | `webhook.webhookId` | string | The subscription's id. Read-only. | | `webhook.url` | string | The HTTPS endpoint deliveries are POSTed to. Plain http URLs and endpoints resolving to private address space are rejected at create and update time. | | `webhook.eventTypes` | string[] | The event types this subscription receives. At least one. Valid values are the v1 taxonomy: `order.created`, `order.updated`, `order.status_changed`, `order.archived`, `quote.created`, `quote.updated`, `quote.status_changed`, `quote.converted_to_order`. Unknown values are rejected naming the offender. | | `webhook.status` | WebhookStatus | Whether the subscription is receiving deliveries. New subscriptions are enabled; see WebhookStatus for how a subscription becomes disabled. The lifecycle state of a webhook subscription. - WEBHOOK_STATUS_ENABLED: The subscription receives deliveries. - WEBHOOK_STATUS_DISABLED: The subscription receives no deliveries. Set manually via UpdateWebhook, or automatically when deliveries exhaust their retries and nothing has been delivered successfully for 7 days (auto-disable; re-enable via UpdateWebhook once the endpoint is healthy). One of: WEBHOOK_STATUS_UNSPECIFIED, WEBHOOK_STATUS_ENABLED, WEBHOOK_STATUS_DISABLED. | | `webhook.description` | string | A free-form label for your own bookkeeping. | | `webhook.secret` | string | The signing secret, in the form `whsec_<43 base64url characters>`. Returned ONLY in the CreateWebhook response — store it then; it cannot be retrieved again (rotation is not available in v1). The FULL string, prefix included, is the HMAC-SHA256 key for verifying the X-Factory-Signature header on deliveries. | | `webhook.createdAt` | string | When the subscription was created. Read-only. | | `webhook.updatedAt` | string | When the subscription was last changed. Read-only. | --- # POST /v1/webhooks/{webhookId}:test **Service:** Webhooks **Operation:** `WebhookService_TestWebhook` POSTs a synthetic `ping` event through the real signing and delivery path and reports what the endpoint answered. The ping is recorded as a `webhook.test` delivery (visible in the delivery log, resendable from the Factory UI) but retries do not apply — this is a live round trip for verifying an endpoint and its signature check. ## Parameters | Parameter | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `webhookId` | path | string | yes | The subscription whose endpoint to ping. | ## Request body `TestWebhookBody` (application/json) ## Responses | Status | Schema | Description | | --- | --- | --- | | 200 | `TestWebhookResponse` | A successful response. | | 400 | `Error` | The request was rejected because it was malformed — for example a webhook id that is not a whsub_ id. The body is a validation-failure envelope: an overall type and message and one entry per problem (each with a stable code, a message, and a param pointing at the offending parameter). | | 401 | `Error` | The request is missing a valid bearer token, or the token is invalid or expired. | | 403 | `Error` | The token is valid but does not permit this action, or the resource belongs to a company the token cannot act for. The error type is permission_denied. | | 404 | `Error` | No webhook subscription with the given id exists for the authenticated company. | | 429 | `Error` | The request was throttled (rate_limited) or exceeded a size limit (resource_exhausted). When throttled, the Retry-After header says how many seconds to wait before retrying; a resource_exhausted request will fail the same way if retried unchanged. | | default | `Error` | Any other error. The body is the same error envelope every error uses: a short stable type identifying the kind of failure, a human-readable message, and the request's idempotency key echoed back when one was supplied. | ## Returns `TestWebhookResponse` | Field | Type | Description | | --- | --- | --- | | `statusCode` | integer | The HTTP status the endpoint returned; 0 when no response arrived (timeout or connection failure). | | `responseSnippet` | string | The start of the endpoint's response body, truncated to 1000 characters. | | `durationMs` | string | How long the round trip took. |