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

---

Source: https://developer.factory.app/guides/push-an-order-in · Factory Sales API v1
