Documentation / Orders

Orders

Orders within a project — the Order object, its products and journal, and every endpoint for listing, creating, updating, deleting and restoring orders.

An order is a deal in a project's sales pipeline. It sits in a status, which belongs to a funnel. It can be assigned to a project member (the responsible user) and linked to a contact. It holds a list of products with their own prices, markups and discounts. Every change is recorded in the order's journal.

Orders are soft-deleted. Deleting an order sets deleted_at and hides it from listings, but its products, journal and warehouse history are kept, and it can be restored.

Field types follow the field notation.

Access

Every endpoint on this page requires both the projects and orders OAuth scopes. The authenticated user must also be a member of the project, and the project owner's subscription must be active. Otherwise the endpoint returns 404 RES_NOT_FOUND.

On top of that, the user's project role must grant one of these permissions. The project owner bypasses permission checks.

Permission Grants
can_view_own_orders Read orders where the user is the responsible member (user_uuid)
can_view_role_orders Read orders whose responsible member has the same role as the user
can_view_all_orders Read every order in the project
can_edit_orders Create, update, delete and restore orders, and add notes to their journal

Every endpoint that addresses a single order, including the editing ones, sees only the orders the user's widest view permission covers. An order outside that range behaves as if it does not exist (404 RES_NOT_FOUND), so editing an order requires can_edit_orders and visibility of that order.

Order Object

Order Structure

Field Type Description
uuid uuid Order ID
serial string Human-readable order number, unique within the project: YYDDDNNN, i.e. year, day of the year and that day's sequence, in the owner's time zone
status_uuid ?uuid Current status. null only if the order's status was deleted. A status can be changed but never cleared through the API
user_uuid ?uuid Responsible project member
user_name string Responsible member's full name; empty string if none
contact_uuid ?uuid Linked contact
contact_name ?string Linked contact's name
loss_reason_uuid ?uuid Why the order was lost; set when moving into a canceled-type status
loss_reason_name ?string Name of the loss reason. Only in Get Order
loss_reason_note ?string Free-text comment on the loss, up to 512 characters
paid_amount decimal Amount already paid
product_count integer Number of product lines
total_amount decimal Sum of all product lines, see Order Product Structure
remaining_amount decimal total_amount − paid_amount
created_at ISO8601 datetime When the order was created
updated_at ISO8601 datetime When the order was last changed
deleted_at ?ISO8601 datetime When the order was soft-deleted; null for active orders

Money amounts are returned as stored, with two decimal places. The project's rounding step (price_step) applies only to display in the app.

Example Order

{
  "uuid": "0199a4e2-7b1c-7c3e-9d2a-5f4e8b1c0a11",
  "serial": "26274003",
  "status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e6f",
  "user_uuid": "0198e7aa-91f0-7c22-a4d1-0f9e8d7c6b5a",
  "user_name": "Anna Petrova",
  "contact_uuid": "0199a4d0-11aa-7b0c-9e8f-7a6b5c4d3e2f",
  "contact_name": "Acme Windows LLC",
  "loss_reason_uuid": null,
  "loss_reason_name": null,
  "loss_reason_note": null,
  "paid_amount": 500,
  "product_count": 2,
  "total_amount": 1470,
  "remaining_amount": 970,
  "created_at": "2026-10-01T09:30:00.000Z",
  "updated_at": "2026-10-01T11:02:41.000Z",
  "deleted_at": null
}

Order Product Structure

A product line on an order. Read lines with List Order Products. Write them through the products array of Create Order or Modify Order.

Field Type Description
product_uuid ?uuid The product; null if the product has since been deleted
product_name ?string Product name
configurator_uuid ?uuid Configurator the product was built with
configuration ?array The configurator selections that make up the product
unit ?string Unit of measure, e.g. pcs
purchase_price decimal Cost price per unit, calculated server-side from the product's materials when the line is saved
sale_price decimal Sale price per unit
markup_percent decimal Markup applied on top of sale_price; can be negative
discount_percent integer Discount, 0–100
quantity decimal Quantity, up to 3 decimal places
total_amount decimal sale_price × quantity × (1 + markup_percent / 100) × (1 − discount_percent / 100)

Lines are returned in the order they were sent.

Order Product Input

The shape of each item in the products array of a request body.

Field Type Description
uuid uuid ID of a product in this project
sale_price? decimal Sale price per unit, ≥ 0. Default 0
markup_percent? decimal Markup, may be negative. Default 0
discount_percent? integer Discount, 0–100. Default 0
quantity? decimal Quantity, ≥ 0. Default 0

If the same product uuid appears more than once, the entries are merged into one line. Their quantities are added up, and the first entry's prices are kept. purchase_price cannot be sent; it is always calculated server-side.

Journal Entry Structure

The journal combines the order's own history with the notes and contact activity linked to it.

Field Type Description
log_type string order for the order's own history, contact for notes and contact activity
user_uuid ?uuid Who made the change; null for system and automation entries
user_name ?string Author's full name
user_email ?string Author's email
contact_uuid ?uuid Contact the entry is linked to
entity_uuid ?uuid The order the entry belongs to
action string What happened, see Journal Actions
message ?string Note text. Empty string for order entries
payload ?object Action details. For created/updated, a field-level diff. Entries made by an automation carry payload.automation with its name
created_at ISO8601 datetime When the entry was recorded

Journal Actions

Action log_type Meaning
created order The order was created
updated order Fields or products changed; payload holds the diff
deleted order The order was soft-deleted
restored order The order was restored
automation_warehouse_movement order An automation moved stock for this order
warehouse_return order Goods from this order were returned to a warehouse
task_created order A task linked to this order was created; see Tasks
task_completed order A task linked to this order was completed
note contact A manual note, see Create Order Note
email contact An email was sent by an automation

Other actions may be added over time. Clients should show unknown actions as generic entries rather than fail.

Example Diff Payload

{
  "order": {
    "status_name": {
      "old": "New",
      "new": "In progress"
    },
    "paid_amount": {
      "old": 0,
      "new": 500
    }
  },
  "products": {
    "added": [
      {
        "key": "Door",
        "changes": {
          "sale_price": {
            "old": null,
            "new": 300
          },
          "quantity": {
            "old": null,
            "new": 1
          }
        },
        "data": {
          "name": "Door",
          "sale_price": 300,
          "quantity": 1,
          "markup_percent": 0,
          "discount_percent": 10,
          "purchase_price": 200
        }
      }
    ],
    "removed": [],
    "changed": [
      {
        "key": "Window",
        "changes": {
          "quantity": {
            "old": 1,
            "new": 2
          }
        },
        "fullData": {
          "name": "Window",
          "sale_price": 100,
          "quantity": 2,
          "markup_percent": 0,
          "discount_percent": 0,
          "purchase_price": 60
        }
      }
    ]
  },
  "old_status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e6f",
  "new_status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e70"
}

old_status_uuid / new_status_uuid are present only when the status changed. A created entry carries only new_status_uuid.

List Orders

GET /api/v1/projects/{project.uuid}/orders

Returns a list of order objects visible to the user, newest first. loss_reason_name is not included.

Requires one of can_view_own_orders, can_view_role_orders, can_view_all_orders.

Query String Params

Field Type Description
page? integer Page number, from 1. Default 1
limit? integer Page size, 1–100. Default 50
search? string Matches the start of serial if numeric, and any part of the contact name (case-insensitive)
statuses? string Status UUIDs separated by ;
include_unassigned? 1 With statuses, also return orders that have no status
members? string Responsible member UUIDs separated by ;
contacts? string Contact UUIDs separated by ;
date_from? ISO8601 datetime Created at or after
date_to? ISO8601 datetime Created at or before
include_deleted? 1 Also return soft-deleted orders

Filters combine with AND. The response is a plain array with no total count. Request the next page until a page comes back shorter than limit.

Example Request

GET /api/v1/projects/0198e7a0-0000-7000-8000-000000000001/orders?statuses=0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e6f&limit=20
Authorization: Bearer <access_token>

Response

200 OK — an array of order objects.

Errors

Status Code When
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role has no order view permission
404 RES_NOT_FOUND The project does not exist or is not accessible

Get Order

GET /api/v1/projects/{project.uuid}/orders/{order.uuid}

Returns a single order, including loss_reason_name. Soft-deleted orders are returned too; check deleted_at.

Requires one of can_view_own_orders, can_view_role_orders, can_view_all_orders.

Response

200 OK — an order object.

Errors

Status Code When
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role has no order view permission
404 RES_NOT_FOUND The project or order does not exist, or the order is outside the user's visibility

Create Order

POST /api/v1/projects/{project.uuid}/orders

Creates an order. The serial is assigned automatically and a created entry is written to the journal. After the order is saved, the project's order_created automations run; order_status_changed automations do not run — they run only when the status changes later. A failing order_created automation does not undo the order; it is recorded in the automation's run history.

Requires can_edit_orders. Passing contact also requires the contacts scope and can_edit_contacts.

JSON Params

Field Type Description
status_uuid? uuid Initial status; must belong to this project. Without it, the status a widget order would get (see Status of new orders)
user_uuid? ?uuid Responsible member; must be a member of the project
contact_uuid? ?uuid Contact; must belong to this project
contact? object The customer, when you have no contact uuid: {name?, email?, phone?, address?} (up to 255/64/32/512 characters). Matched by email or phone like a widget order, otherwise created. Not together with contact_uuid
paid_amount? decimal Amount already paid, ≥ 0. Default 0
products? array Product lines: existing products and configured products in any mix

Creating an order does not check the status's required fields or loss-reason rule; those apply only when the status changes.

Configured Product Input

A product that does not exist yet: the server builds it from the configurator — name, configuration, bill of materials, cut-list and prices — with the same calculation the widget uses, and reuses an identical existing product if there is one.

Field Type Description
configurator_uuid uuid A configurator of this project
values? object Control values by control id, as in Calculate; missing controls take their default. A materials control takes {"<material uuid>": {"quantity": 2}}
sale_price? decimal Sale price per unit, ≥ 0. Default: the calculated sale price
markup_percent? decimal Markup, may be negative. Default 0
discount_percent? integer Discount, 0–100. Default 0
quantity? decimal Quantity, ≥ 0. Default 1

purchase_price is calculated. Configured lines follow the existing-product lines in the order.

Example Request

{
  "status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e6f",
  "user_uuid": "0198e7aa-91f0-7c22-a4d1-0f9e8d7c6b5a",
  "contact_uuid": "0199a4d0-11aa-7b0c-9e8f-7a6b5c4d3e2f",
  "paid_amount": 500,
  "products": [
    {
      "uuid": "0199a3f1-5c2e-7d40-b1a2-3c4d5e6f7a8b",
      "sale_price": 450,
      "quantity": 2,
      "discount_percent": 5
    },
    {
      "uuid": "0199a3f1-77d0-7a11-8c9b-0a1b2c4d5e6f",
      "sale_price": 615,
      "quantity": 1
    }
  ]
}

Example Request: order from a website

{
  "contact": {
    "name": "John Smith",
    "email": "[email protected]",
    "phone": "+1 555 010 3000"
  },
  "products": [
    {
      "configurator_uuid": "0199ab16-3d4e-7f50-8162-738495061728",
      "values": {
        "$width": 1200,
        "$height": 1600
      },
      "quantity": 2
    },
    {
      "configurator_uuid": "0199ab16-9a01-7c3e-8d2f-1b2c3d4e5f60",
      "values": {
        "$length": 3000
      }
    }
  ]
}

Response

201 Created

{
  "uuid": "0199a4e2-7b1c-7c3e-9d2a-5f4e8b1c0a11"
}

Errors

Status Code When
400 REQ_VALIDATION_FAILED The body does not match the schema
400 ORDER_STATUS_NOT_FOUND status_uuid is not a status of this project
400 ORDER_CONTACT_NOT_FOUND contact_uuid is not a contact of this project
400 ORDER_USER_NOT_MEMBER user_uuid is not a member of this project
400 RES_NOT_FOUND A product uuid does not exist in this project
400 ORDER_PRODUCT_NOT_PRICED A configured product cannot be calculated: no such configurator, or an unknown control id in values; details names the item
400 ORDER_STATUS_NOT_FOUND No status_uuid was given and the project has no statuses
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders, or contacts when contact is passed
403 AUTH_NO_PROJECT_ACCESS The user's role lacks can_edit_orders, or can_edit_contacts when contact is passed
404 RES_NOT_FOUND The project does not exist or is not accessible

Modify Order

PATCH /api/v1/projects/{project.uuid}/orders/{order.uuid}

Updates only the fields you send. If products is sent, it replaces the whole product list; omit it to leave the lines untouched. Changes are recorded in the journal as an updated entry.

Requires can_edit_orders.

JSON Params

All fields are optional, but at least one must be present.

Field Type Description
status_uuid? uuid New status, see Status changes. Sending null is rejected
user_uuid? ?uuid Responsible member; null unassigns
contact_uuid? ?uuid Contact; null unlinks. The order's notes move with it to the new contact
paid_amount? decimal ≥ 0
products? array of order product input Replaces all product lines; [] removes every line
loss_reason_uuid? ?uuid Loss reason; must belong to this project
loss_reason_note? string Up to 512 characters

Status changes

When status_uuid differs from the current status:

  1. The status cannot be cleared. null returns ORDER_STATUS_REQUIRED. An order whose status was deleted keeps status_uuid: null and can still be edited, as long as status_uuid is not sent.
  2. Transition rules. If the target funnel has a transition matrix, the move must be allowed by it. Otherwise the request fails with ORDER_STATUS_TRANSITION_NOT_ALLOWED. Moving to a status in a different funnel is always allowed.
  3. Loss reason. Moving into a canceled-type status requires loss_reason_uuid, sent with the request or already on the order, whenever the project has at least one loss reason configured.
  4. Required fields. If the target status lists required contact fields, the order's contact (the new one if contact_uuid is sent) must have all of them filled in. With no contact, every required field counts as missing.
  5. Automations. order_status_changed automations run inside the same transaction, after the product list is saved. If any automation fails (for example, a warehouse write-off without enough stock), the whole update is rolled back and the status does not change.

Example Request

{
  "status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e70",
  "paid_amount": 1470
}

Response

204 No Content

Errors

Status Code When
400 REQ_VALIDATION_FAILED The body does not match the schema
400 REQ_NO_DATA_PROVIDED The body has no fields
400 ORDER_STATUS_REQUIRED status_uuid is null
400 ORDER_STATUS_NOT_FOUND status_uuid is not a status of this project
400 ORDER_STATUS_TRANSITION_NOT_ALLOWED The funnel's transition matrix forbids this move
400 ORDER_LOSS_REASON_REQUIRED Moving into a canceled status without a loss reason
400 ORDER_REQUIRED_FIELD_MISSING The contact is missing fields the status requires; the response lists them in fields
400 ORDER_CONTACT_NOT_FOUND contact_uuid is not a contact of this project
400 ORDER_USER_NOT_MEMBER user_uuid is not a member of this project
400 ORDER_LOSS_REASON_NOT_FOUND loss_reason_uuid is not a loss reason of this project
400 RES_NOT_FOUND A product uuid does not exist in this project
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role lacks can_edit_orders
404 RES_NOT_FOUND The project or order does not exist, or the order is outside the user's visibility
409 ORDER_DELETED The order is soft-deleted; restore it first
409 ORDER_INSUFFICIENT_STOCK A status-change automation could not write off stock; see below

Example Error: Validation

REQ_VALIDATION_FAILED lists every problem in errors. path points to the offending field.

{
  "id": 2001,
  "code": "REQ_VALIDATION_FAILED",
  "message": "The request was improperly formatted or contained invalid data.",
  "errors": [
    {
      "origin": "number",
      "code": "too_big",
      "maximum": 100,
      "inclusive": true,
      "path": [
        "products",
        0,
        "discount_percent"
      ],
      "message": "Too big: expected number to be <=100"
    }
  ]
}

Example Error: Required Fields

{
  "id": 4007,
  "code": "ORDER_REQUIRED_FIELD_MISSING",
  "message": "One or more fields required for this status are not filled in.",
  "fields": [
    "inn",
    "delivery_address"
  ]
}

Example Error: Insufficient Stock

{
  "id": 4005,
  "code": "ORDER_INSUFFICIENT_STOCK",
  "message": "Not enough stock to complete this operation.",
  "warehouse_uuid": "0199a0b1-3c4d-7e5f-8a9b-0c1d2e3f4a5b",
  "shortages": [
    {
      "material_uuid": "0199a0c2-1111-7222-8333-444455556666",
      "item_type": "material",
      "name": "Aluminium profile 60mm",
      "unit": "m",
      "needed": 12.5,
      "available": 8
    }
  ]
}

Replace Order

PUT /api/v1/projects/{project.uuid}/orders/{order.uuid}

Same as Modify Order, except that the full object is required.

Requires can_edit_orders.

JSON Params

Field Type Description
status_uuid ?uuid Must be sent. null is accepted only if the order already has no status
user_uuid ?uuid Must be sent; null unassigns
contact_uuid ?uuid Must be sent; null unlinks
paid_amount decimal ≥ 0
products? array of order product input Replaces all lines. Omitting it removes every line
loss_reason_uuid? ?uuid Loss reason
loss_reason_note? string Up to 512 characters

The status change rules apply in the same way.

Response

204 No Content

Errors

Same as Modify Order, except REQ_NO_DATA_PROVIDED.

Delete Order

DELETE /api/v1/projects/{project.uuid}/orders/{order.uuid}

Soft-deletes the order: sets deleted_at and removes it from List Orders unless include_deleted=1 is passed. Products, journal and warehouse history are kept. A deleted entry is added to the journal.

Requires can_edit_orders.

Response

204 No Content

Errors

Status Code When
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role lacks can_edit_orders
404 RES_NOT_FOUND The project or order does not exist, is already deleted, or is outside the user's visibility

Restore Order

POST /api/v1/projects/{project.uuid}/orders/{order.uuid}/restore

Restores a soft-deleted order by clearing deleted_at. A restored entry is added to the journal.

Requires can_edit_orders.

Response

204 No Content

Errors

Status Code When
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role lacks can_edit_orders
404 RES_NOT_FOUND The project or order does not exist, the order is not deleted, or it is outside the user's visibility

List Order Products

GET /api/v1/projects/{project.uuid}/orders/{order.uuid}/products

Returns the order's product lines as an array of order product objects.

Requires one of can_view_own_orders, can_view_role_orders, can_view_all_orders.

Example Response

[
  {
    "product_uuid": "0199a3f1-5c2e-7d40-b1a2-3c4d5e6f7a8b",
    "product_name": "PVC window 1200×1400",
    "configurator_uuid": "0199a3e0-0a0b-7c0d-8e0f-101112131415",
    "configuration": [],
    "unit": "pcs",
    "purchase_price": 310,
    "sale_price": 450,
    "markup_percent": 0,
    "discount_percent": 5,
    "quantity": 2,
    "total_amount": 855
  }
]

Errors

Status Code When
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role has no order view permission
404 RES_NOT_FOUND The project or order does not exist, or the order is outside the user's visibility

Get Order Journal

GET /api/v1/projects/{project.uuid}/orders/{order.uuid}/logs

Returns up to 50 journal entries, oldest first. The list includes the order's own history and any notes on the order. With contact_uuid, it also includes that contact's whole activity history.

Requires one of can_view_own_orders, can_view_role_orders, can_view_all_orders.

Query String Params

Field Type Description
contact_uuid? uuid Also include this contact's activity
last_log? ISO8601 datetime Return only entries created strictly after this time, for incremental polling

To poll for new entries, pass the created_at of the last entry you have as last_log.

Response

200 OK — an array of journal entry objects.

Errors

Status Code When
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role has no order view permission
404 RES_NOT_FOUND The project or order does not exist, or the order is outside the user's visibility

Create Order Note

POST /api/v1/projects/{project.uuid}/orders/{order.uuid}/logs

Adds a note to the order's journal. The order does not need a contact. If a contact is linked later, the note moves to that contact's history automatically.

Requires can_edit_orders, and the order must be within the user's visibility.

JSON Params

Field Type Description
message string Note text, 1–5000 characters (whitespace trimmed)

Example Request

{
  "message": "Customer asked to move delivery to next Friday."
}

Response

201 Created with an empty body.

Errors

Status Code When
400 REQ_VALIDATION_FAILED message is missing, empty or too long
403 AUTH_INSUFFICIENT_SCOPE The token lacks projects or orders
403 AUTH_NO_PROJECT_ACCESS The user's role lacks can_edit_orders
404 RES_NOT_FOUND The project or order does not exist, the order is deleted, or it is outside the user's visibility