Creating Orders
The full Create Order request, what is required, and the optional data you can prefill upfront
Overview
The Quick Start creates a minimal order. This page is the complete reference for the Create Order request body.
Most of the data an order needs is collected as it moves through its workflow: the operator or auto-dispatch assigns an installer, the customer answers questions and uploads files in their portal, and they review and pay the offer. See Orders for how that flow works. So creating an order via the API requires only a small set of fields to get it started.
Everything beyond that minimum is optional prefill. If your own system already holds a piece of data the workflow would otherwise gather (a custom field answer, the installer, the priced offer), send it at creation to skip or speed up the corresponding step. These are advanced options: useful when you know the data upfront, safe to leave out when you don't.
All examples below are fragments of the same JSON body posted to POST https://api.installer.com/api/v1/order with an Authorization: Bearer <token> header. Combine the fragments you need into one object. See the API Reference for the exhaustive schema.
Required fields
Every order needs a workflow template reference plus the customer contact and installation address:
| Field | Type | Notes |
|---|---|---|
| Workflow reference | String | A workflowId or workflowCustomId (see below). If both are omitted, your organisation's default published template is used. |
contactPersonName | String | 1 to 150 characters. |
email | String | Valid email address. |
phoneNumber | String | E.164 format, for example +4712345678. Spaces are stripped. |
address | String | Street address, up to 250 characters. |
city | String | Up to 150 characters. |
postalCode | String | Up to 50 characters. |
countryCode | String | ISO 3166-1 alpha-2, for example NO, SE, GB. |
A minimal, valid request:
{
"workflowId": "0b6f...uuid",
"contactPersonName": "Jane Doe",
"email": "[email protected]",
"phoneNumber": "+4712345678",
"address": "Storgata 1",
"city": "Oslo",
"postalCode": "0155",
"countryCode": "NO"
}Targeting a workflow template
The workflow template defines the steps the order runs through and the questions it collects. Reference it one of three ways:
workflowId(recommended): the template's UUID, shown in the "Send orders via API" dialog in the Routing App. Resolves to the currently published version of that template.workflowCustomId: a stable custom ID you assign to the template. Targets the published template with that custom ID in your organisation. Prefer this if you want a human-readable identifier that survives template recreation.- Neither: the order falls back to your organisation's default published template.
Send workflowId or workflowCustomId, not both. If the reference doesn't resolve to a published template, the request is rejected.
Prefilling workflow data (optional)
The fields in this section are normally collected from the operator, installer, or customer as the order progresses through its workflow. Prefill them only when your system already knows the answer upfront. Anything you leave out is simply gathered later, in the relevant step.
Custom fields
Custom fields are the answers to the questions on your workflow template. Normally the customer or installer fills them in during the order; if your CRM, lead form, or configurator already holds them, send them with customFields. Each entry references a field by its customId (recommended: the stable kebab-case ID configured on the template) or by id (the field's UUID), together with the value:
{
"customFields": [
{ "customId": "mpan", "value": "S12345678901234567890" },
{ "customId": "panel-count", "value": 12 },
{ "customId": "scaffolding-required", "value": true },
{ "customId": "preferred-install-date", "value": "2026-08-01" },
{ "customId": "site-photo", "value": "e87a0842-85c7-4743-928f-03942d69f82f.jpg" }
]
}Prefilled fields show up as already-answered questions in the workflow, so nobody has to ask the customer again.
Value format per field type
Each custom field has a type, configured on the workflow template. Send the JSON value that matches the type:
| Field type | What to send as value | Example |
|---|---|---|
| Text | String | { "customId": "meter-location", "value": "Fuse box in garage" } |
| String (email address) | { "customId": "site-contact-email", "value": "[email protected]" } | |
| Numeric | Number, or a numeric string | { "customId": "panel-count", "value": 12 } |
| Boolean | Boolean (true/false) | { "customId": "scaffolding-required", "value": true } |
| Date | ISO 8601 date string | { "customId": "preferred-install-date", "value": "2026-08-01" } |
| Datetime | ISO 8601 datetime string | { "customId": "survey-slot", "value": "2026-08-01T09:00:00Z" } |
| File | File ID from the upload endpoint | { "customId": "site-photo", "value": "e87a0842-85c7-4743-928f-03942d69f82f.jpg" } |
Values are coerced to the field's type: "12" works for a Numeric field and the string "false" is treated as false for a Boolean field. A value that can't be coerced (for example "abc" on a Numeric field) is stored as empty rather than rejected.
Entries whose customId or id doesn't match a custom field on the order's workflow template are silently skipped, not rejected. If a prefilled answer isn't showing up on the order, check that the custom ID matches the field configured on the template (the "Send orders via API" dialog in the Routing App lists them).
File fields: upload first, then reference
File answers wire together in two steps: upload the file, then pass the returned file ID as the custom field value.
1. Upload the file to the upload endpoint as multipart/form-data, using the same API token. The file form field carries the file content:
curl -X POST https://api.installer.com/api/v1/file/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "[email protected]"The response contains the file ID:
{ "value": "e87a0842-85c7-4743-928f-03942d69f82f.jpg" }2. Reference the file ID as the value of a File custom field when creating the order:
{
"customFields": [
{ "customId": "site-photo", "value": "e87a0842-85c7-4743-928f-03942d69f82f.jpg" }
]
}The platform links the uploaded file to the field, and it appears as the answer to that question (with preview and download) in the order.
Multiple answers per field
For fields that take several answers, most commonly multiple files on one question, send a values array instead of a single value:
{
"customFields": [
{
"customId": "site-photos",
"values": [
{ "value": "e87a0842-85c7-4743-928f-03942d69f82f.jpg" },
{ "value": "9c2d1b7e-4f6a-4e2b-8d3c-5a1e0f9b6c7d.jpg" }
]
}
]
}Send either value or values for a field, not both. If both are present, value wins and values is ignored.
Order-level files vs. file custom fields
The create order payload also accepts a top-level files array. That attaches general documents to the order's file list without answering any question:
{
"files": [
{ "fileId": "3b8e2f10-6c4d-4a9b-b5e7-2d0c8a4f1e6b.pdf", "fileName": "floor-plan.pdf" }
]
}Use a File custom field when the file is the answer to a specific question in your workflow. Use the top-level files array for general attachments like site plans or contracts. Both take file IDs from the same POST /api/v1/file/upload endpoint.
Actor assignment
Choosing the installer is normally done inside the platform at the workflow's Select Installer step, either manually by the operator or automatically via auto-dispatch, so most integrations don't set this at all. If you already know which installer should perform the job (for example, you've assigned the lead to a specific partner in your CRM), you can pre-assign them with actorAssignments to fulfil that step immediately:
{
"actorAssignments": [
{
"actorSlug": "installer",
"organizationExternalId": "partner-acme-123"
}
]
}actorSlugmatches an actor configured on your workflow template (typicallyinstaller).- Identify the organisation by either
organizationId(Installer.com UUID) ororganizationExternalId(the ID you use for the partner in your own system).
actorAssignments and the Dispatch Order endpoint do the same job from the API: both assign the installer and fulfil the Select Installer step. Use actorAssignments when you already know the installer at creation time, and Dispatch Order to route an order that already exists (for example once your own system decides who should take it).
If you do neither, the order simply waits at its Select Installer step for the operator or auto-dispatch to route it. Pre-assign only when your CRM already owns that decision; otherwise, let the platform handle it. See Creating an order for how routing normally happens.
Offer
An order's priced quote is normally prepared and presented, then accepted and paid, at the workflow's offer and payment steps. Prefilling the offer at creation means the customer sees pricing immediately, and acceptance plus payment in the platform becomes your conversion event. Set payer to client (the end customer pays) or operator (your organisation pays).
There are two ways to populate offer lines, and one of them is strongly preferred.
Recommended: reference products from your catalogue
Pass a productId or externalId on each offer line. The platform matches against your product catalogue and pulls in pricing, payout, and tax configuration automatically:
{
"offer": {
"name": "EV charger install",
"payer": "client",
"lines": [
{ "externalId": "evc-7kw", "quantity": 1 },
{ "productId": "5f...uuid", "quantity": 2 }
]
}
}Why this matters:
- Pricing groups per installation partner: products in Installer.com support per-partnership pricing. The same product can be priced differently for different contractors, with the right unit amount, tax rate, and installer payout applied automatically when the order is dispatched.
- Payment and split payout: when the customer pays, the platform splits the payment between your organisation and the assigned installer based on the product's configured payout. This only works correctly when the offer line is tied to a product.
- Catalogue updates flow through: if you change a product's price, future orders reflect it without integration changes on your side.
Set up your product catalogue in Settings → Products in the Routing App, then reference products by externalId (recommended for stable cross-system identifiers) or productId.
Fallback: raw offer lines
You can also pass raw line details (name, unit amount, VAT rate, payout) directly without referencing a product:
{
"offer": {
"lines": [
{
"name": "EV charger",
"quantity": 1,
"currencyCode": "NOK",
"payerUnitAmount": "1000000",
"vatRate": "2500",
"installerPayoutUnitAmount": "500000"
}
]
}
}Raw offer lines are not well-supported when the workflow includes payments with split payout to installers. Per-partner pricing, payout configuration, and catalogue updates all live on the product. Use raw lines only for one-off line items that don't fit your catalogue, and prefer the product reference approach for anything you'll create more than once.
Additional order fields (optional)
These refine the order record and how the request behaves. All are optional.
Extra customer and location details
| Field | Type | Notes |
|---|---|---|
state | String | Region or state, up to 16 characters. |
latitude / longitude | Number | Job-site coordinates. Provide both, or omit both and let the platform geocode the address. |
customerLegalId | String | The customer's legal or organisation number, up to 50 characters. |
externalId | String | Your own reference for this order in your system (for example a CRM lead ID or accounting project reference). Up to 100 characters. |
property | Object | Property identifier for markets that use one (see below). |
The property object is a tagged union. Send a type plus the fields for that type:
{
"property": { "type": "FASTIGHETSBETECKNING", "externalId": "Gustavsberg 1:23" }
}{
"property": { "type": "BRF", "externalId": "769600-1234", "apartmentNumber": "1102" }
}Order details and control
| Field | Type | Notes |
|---|---|---|
name | String | Display name for the order, up to 150 characters. If omitted, a name is generated. |
description | String or object | Order description. Plain text up to 5000 characters, or a rich-text object. If omitted, the workflow template's default description is used. |
metadata | Object | Arbitrary key/value pairs stored on the order and available in webhook and automation payloads. Use string values. |
orderTagIds | String array | Tag UUIDs to assign at creation. List available tags with List Order Tags. |
partnershipId | String | ID of the partnership relaying this order to a partner. Leave blank for direct submissions. |
isTest | Boolean | Marks the order as a test order for trying out a workflow template. Test orders run against the exact template version you are editing, including unpublished drafts. |
idempotencyKey | String | See below. |
Idempotency
Set idempotencyKey to a unique value (ideally a UUID, up to 150 characters) so retries don't create duplicate orders. If a request arrives with an idempotency key that already produced an order, the API responds with 409 Conflict and the existing orderId and displayId instead of creating a second order. If you omit the key, no duplicate check is performed.
Next steps
- Orders for the workflow model, step types, and lifecycle.
- Integrating with External Systems for the broader integration model, including outbound webhooks and automation hooks.
- Webhooks to receive workflow state changes back in your system.
- API Reference for the full Create Order schema.