Work orders

Jobs (uppdrag): create them from a webshop or CRM, follow them through their statuses, complete them from the field with checklist, materials and signature, and read what they are worth.

A job's life: booked → scheduled (somebody assigned) → in_progress → completed → invoiced, or cancelled before it is done. All times are UTC instants. Times of day that people set — a recurrence's start_time, an employee's working hours — are Swedish local time (Europe/Stockholm), and schedule warnings compare a job with them on that clock.

List work orders

GET/work-orders

Work orders (uppdrag) in schedule order: planned jobs by planned_start, then unplanned ones.

To read a calendar, pass a window with from and to. The window is an overlap test on the planned times, so a job that starts before from and is still running at from is included, and a two-day job appears on both weeks it touches. A window leaves out unplanned jobs (they have no start): ask for those with filter[unplanned]=1.

Each work order includes its customer, service address and assignments; read one work order for its contact, services, checklist and materials. Cursor-paginated: follow links.next, or pass meta.next_cursor as cursor.

curl 'https://api.closepact.com/v1/api/work-orders' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
      "order_number": "1042",
      "title": "Flyttstädning – Storgatan 12, Uppsala",
      "description": "Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.",
      "status": "scheduled",
      "priority": 1,
      "source": "manual",
      "handover": null,
      "customer": {
        "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
        "name": "Brf Linden",
        "customer_number": "10231"
      },
      "service_address": {
        "id": "01a15c02-4e1c-7d5e-9f40-7182c9daeb23",
        "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
        "account_id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
        "type": "service",
        "label": "Lägenhet 1102",
        "street": "Storgatan 12",
        "street2": "Lgh 1102, 3 tr",
        "zip": "753 20",
        "city": "Uppsala",
        "country": "SE",
        "lat": 59.8586,
        "lng": 17.6389,
        "geofence_radius_m": 150,
        "access_notes": "Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.",
        "contact_id": "01a15c02-4e1b-7c4d-8e3f-6071b8c9da12",
        "is_default": true,
        "metadata": [],
        "created_at": "2026-09-02T09:15:00.000000Z",
        "updated_at": "2026-09-02T09:15:00.000000Z",
        "deleted_at": null
      },
      "pricing": {
        "model": "hourly",
        "agreed_price_cents": null,
        "currency": "SEK",
        "rot_rut": "rut",
        "rot_rut_share": 100
      },
      "schedule": {
        "planned_start": "2026-11-03T07:00:00+00:00",
        "planned_end": "2026-11-03T11:00:00+00:00",
        "earliest_start": "2026-11-03T07:00:00+00:00",
        "latest_start": "2026-11-03T09:00:00+00:00",
        "estimated_minutes": 240,
        "actual_start": null,
        "actual_end": null,
        "actual_minutes": null
      },
      "assignments": [
        {
          "id": "01a15c11-0b1c-7f20-b162-93a4ebf10d45",
          "role": "lead",
          "status": "assigned",
          "notified_at": "2026-10-28T13:02:11+00:00",
          "accepted_at": null,
          "employee": {
            "id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01",
            "name": "Ali Hassan",
            "color": "#2F80ED"
          }
        }
      ],
      "completed_at": null,
      "cancelled_at": null,
      "cancel_reason": null,
      "customer_signed_at": null,
      "created_at": "2026-10-28T13:01:55+00:00",
      "updated_at": "2026-10-28T13:02:11+00:00"
    }
  ],
  "links": {
    "first": null,
    "last": null,
    "prev": null,
    "next": null
  },
  "meta": {
    "path": "https://api.closepact.com/v1/api/work-orders",
    "per_page": 50,
    "next_cursor": null,
    "prev_cursor": null
  }
}

Query parameters

  • qstring

    Search in the title and the order number (case-insensitive, substring).

    Example Storgatan

  • filter[status]string

    Only these statuses. Repeat as filter[status][]=booked&filter[status][]=scheduled for several.

    One ofbookedscheduledin_progresscompletedinvoicedcancelled

    Example scheduled

  • filter[account_id]string(uuid)

    Only this customer's work orders.

  • filter[employee_id]string(uuid)

    Only work orders this employee is assigned to.

  • filter[unplanned]boolean

    1 for only jobs with no planned start.

  • fromstring(date-time)

    Window start: jobs still planned to be running at or after this instant (or with no planned end). A date (2026-11-02) means midnight UTC. ISO 8601.

    Example 2026-11-02T00:00:00Z

  • tostring(date-time)

    Window end: jobs planned to start at or before this instant. A bare date means midnight UTC at the start of that day, so to include all of Sunday send 2026-11-08T23:59:59Z. ISO 8601.

    Example 2026-11-08T23:59:59Z

  • limitinteger

    Items per page, 1–200.

    Default 501–200

  • cursorstring

    Opaque cursor from meta.next_cursor or meta.prev_cursor of the previous page.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataarray of objectsrequired
      • idstring(uuid)required

        The work order id.

      • order_numberstringrequired

        Number from the organization's work order series, unique within the organization (format set in the panel, e.g. 1042 or WO-01042).

      • titlestringrequired

        What the job is, as shown on the board.

      • descriptionstringnullablerequired

        Free text for the people doing the job.

      • statusstringrequired

        booked: promised to the customer, nobody placed yet. scheduled: somebody is going. in_progress: they are there. completed: the work is done (and billable). invoiced: the money has been asked for. cancelled: not happening.

        One ofbookedscheduledin_progresscompletedinvoicedcancelled

      • priorityintegerrequired

        -2 (lowest) to 2 (highest); 0 is normal.

        -2–2

      • sourcestringrequired

        Where the job came from: manual (created in the panel or this API), recurrence (written by a recurrence) or quote (made from an accepted quote — see handover). More values may be added.

      • handoverobjectnullablerequired

        The accepted quote this work order was made from, or null when it was not.

      • customerobjectrequired

        The customer the job is for (a customer from the Customers API). Still shown after that customer is deleted: a deleted customer keeps its past jobs.

      • contactobjectnullable

        The customer contact for this job, or null. Included by GET /work-orders/{workOrder} only.

      • service_addressobjectnullable

        Where the work is done, or null. Included by the list, GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • pricingobjectrequired
      • scheduleobjectrequired

        When the job is planned, what was promised, and when it actually ran. All UTC.

      • assignmentsarray of objects

        Who is going. Included everywhere except the response to creating a work order.

      • servicesarray of objects

        Services ordered for the job, priced as they were when ordered. Included by GET /work-orders/{workOrder} only.

      • checklistarray of objects

        The steps to tick, in sort_order. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • materialsarray of objects

        Materials used. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete; GET /work-orders/{workOrder}/materials returns the same lines with net_cents.

      • completed_atstring(date-time)nullablerequired

        When the job was completed, or null.

      • cancelled_atstring(date-time)nullablerequired

        When the job was cancelled, or null.

      • cancel_reasonstringnullablerequired

        The reason given when cancelling, or null.

      • customer_signed_atstring(date-time)nullablerequired

        When the customer signed on completion, or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

    • linksobjectrequired
    • metaobjectrequired
  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Create a work order

POST/work-orders
Scope write Module Scheduling & work orders Permission booking.work_orders.manage

Creates a job for one of your customers — from a webshop order, a CRM deal or a booking form. It starts as booked and takes the next number in the organization's work order series.

Giving it planned_start/planned_end puts it on the calendar but assigns nobody; to send somebody, call POST /schedule/assign, which also moves the job to scheduled. The status cannot be set here: see POST /work-orders/{workOrder}/status.

account_id, contact_id, service_address_id and sub_entity_id must belong to your organization; anything else is a 422. Amounts are öre excluding VAT.

curl -X POST 'https://api.closepact.com/v1/api/work-orders' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "account_id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
  "title": "Flyttstädning – Storgatan 12, Uppsala",
  "contact_id": "01a15c02-4e1b-7c4d-8e3f-6071b8c9da12",
  "service_address_id": "01a15c02-4e1c-7d5e-9f40-7182c9daeb23",
  "description": "Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.",
  "priority": 1,
  "rot_rut": "rut",
  "planned_start": "2026-11-03T08:00:00+01:00",
  "planned_end": "2026-11-03T12:00:00+01:00",
  "estimated_minutes": 240
}'
Response · 201
{
  "data": {
    "id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "order_number": "1042",
    "title": "Flyttstädning – Storgatan 12, Uppsala",
    "description": "Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.",
    "status": "booked",
    "priority": 1,
    "source": "manual",
    "handover": null,
    "customer": {
      "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "name": "Brf Linden",
      "customer_number": "10231"
    },
    "pricing": {
      "model": "hourly",
      "agreed_price_cents": null,
      "currency": "SEK",
      "rot_rut": "rut",
      "rot_rut_share": 100
    },
    "schedule": {
      "planned_start": "2026-11-03T07:00:00+00:00",
      "planned_end": "2026-11-03T11:00:00+00:00",
      "earliest_start": "2026-11-03T07:00:00+00:00",
      "latest_start": "2026-11-03T09:00:00+00:00",
      "estimated_minutes": 240,
      "actual_start": null,
      "actual_end": null,
      "actual_minutes": null
    },
    "completed_at": null,
    "cancelled_at": null,
    "cancel_reason": null,
    "customer_signed_at": null,
    "created_at": "2026-10-28T13:01:55+00:00",
    "updated_at": "2026-10-28T13:01:55+00:00"
  }
}

Request body

application/json · required

  • account_idstring(uuid)required

    The customer (a customer id from the Customers API). Must be one of your organization's.

  • titlestringrequired

    What the job is.

    at most 255 characters

  • contact_idstring(uuid)nullable

    A contact at the customer, or null.

  • service_address_idstring(uuid)nullable

    Where the work is done: one of the customer's addresses, or null.

  • descriptionstringnullable

    Free text for the people doing the job.

  • priorityinteger

    -2 to 2. Default 0.

    -2–2

  • price_modelstring

    How the customer is charged: by approved hours, a fixed agreed_price_cents, or per unit of the ordered services.

    One ofhourlyfixedper_unit

    Default "hourly"

  • agreed_price_centsintegernullable

    Agreed price for a fixed job, in öre excluding VAT.

    ≥ 0

  • rot_rutstring

    Swedish tax deduction on the labour: ROT (repairs, 30 %) or RUT (household services, 50 %), computed on the invoice basis.

    One ofnonerotrut

    Default "none"

  • rot_rut_sharenumbernullable

    Share of the deduction this customer claims, 0–100 percent.

    0–100

  • planned_startstring(date-time)nullable

    ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Null for unplanned work.

  • planned_endstring(date-time)nullable

    ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Must be after planned_start.

  • earliest_startstring(date-time)nullable

    Customer's window, earliest acceptable start. ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC.

  • latest_startstring(date-time)nullable

    Customer's window, latest acceptable start; not before earliest_start. ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC.

  • estimated_minutesintegernullable

    Expected duration in minutes.

    ≥ 1

  • sub_entity_idstring(uuid)nullable

    The office (sub-entity) the job belongs to, for organizations with several. Must be one of yours.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A job (uppdrag): what is to be done, for which customer, where and when, and by whom. Which nested parts are included depends on the endpoint — see each property.

      • idstring(uuid)required

        The work order id.

      • order_numberstringrequired

        Number from the organization's work order series, unique within the organization (format set in the panel, e.g. 1042 or WO-01042).

      • titlestringrequired

        What the job is, as shown on the board.

      • descriptionstringnullablerequired

        Free text for the people doing the job.

      • statusstringrequired

        booked: promised to the customer, nobody placed yet. scheduled: somebody is going. in_progress: they are there. completed: the work is done (and billable). invoiced: the money has been asked for. cancelled: not happening.

        One ofbookedscheduledin_progresscompletedinvoicedcancelled

      • priorityintegerrequired

        -2 (lowest) to 2 (highest); 0 is normal.

        -2–2

      • sourcestringrequired

        Where the job came from: manual (created in the panel or this API), recurrence (written by a recurrence) or quote (made from an accepted quote — see handover). More values may be added.

      • handoverobjectnullablerequired

        The accepted quote this work order was made from, or null when it was not.

      • customerobjectrequired

        The customer the job is for (a customer from the Customers API). Still shown after that customer is deleted: a deleted customer keeps its past jobs.

      • contactobjectnullable

        The customer contact for this job, or null. Included by GET /work-orders/{workOrder} only.

      • service_addressobjectnullable

        Where the work is done, or null. Included by the list, GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • pricingobjectrequired
      • scheduleobjectrequired

        When the job is planned, what was promised, and when it actually ran. All UTC.

      • assignmentsarray of objects

        Who is going. Included everywhere except the response to creating a work order.

      • servicesarray of objects

        Services ordered for the job, priced as they were when ordered. Included by GET /work-orders/{workOrder} only.

      • checklistarray of objects

        The steps to tick, in sort_order. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • materialsarray of objects

        Materials used. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete; GET /work-orders/{workOrder}/materials returns the same lines with net_cents.

      • completed_atstring(date-time)nullablerequired

        When the job was completed, or null.

      • cancelled_atstring(date-time)nullablerequired

        When the job was cancelled, or null.

      • cancel_reasonstringnullablerequired

        The reason given when cancelling, or null.

      • customer_signed_atstring(date-time)nullablerequired

        When the customer signed on completion, or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Get a work order

GET/work-orders/{workOrder}

One work order with everything on it: customer, contact, service address, assignments, ordered services, checklist and materials. A work order of another organization, or a deleted one, is a 404.

curl 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response · 200
{
  "data": {
    "id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "order_number": "1042",
    "title": "Flyttstädning – Storgatan 12, Uppsala",
    "description": "Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.",
    "status": "scheduled",
    "priority": 1,
    "source": "manual",
    "handover": null,
    "customer": {
      "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "name": "Brf Linden",
      "customer_number": "10231"
    },
    "contact": {
      "id": "01a15c02-4e1b-7c4d-8e3f-6071b8c9da12",
      "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
      "account_id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "email": "karin.ek@brflinden.se",
      "name": "Karin Ek",
      "title": "Ordförande",
      "phone": "070-412 33 08",
      "crm_external_id": null,
      "crm_provider": null,
      "metadata": null,
      "created_at": "2026-09-02T09:14:00.000000Z",
      "updated_at": "2026-09-02T09:14:00.000000Z",
      "deleted_at": null
    },
    "service_address": {
      "id": "01a15c02-4e1c-7d5e-9f40-7182c9daeb23",
      "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
      "account_id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "type": "service",
      "label": "Lägenhet 1102",
      "street": "Storgatan 12",
      "street2": "Lgh 1102, 3 tr",
      "zip": "753 20",
      "city": "Uppsala",
      "country": "SE",
      "lat": 59.8586,
      "lng": 17.6389,
      "geofence_radius_m": 150,
      "access_notes": "Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.",
      "contact_id": "01a15c02-4e1b-7c4d-8e3f-6071b8c9da12",
      "is_default": true,
      "metadata": [],
      "created_at": "2026-09-02T09:15:00.000000Z",
      "updated_at": "2026-09-02T09:15:00.000000Z",
      "deleted_at": null
    },
    "pricing": {
      "model": "hourly",
      "agreed_price_cents": null,
      "currency": "SEK",
      "rot_rut": "rut",
      "rot_rut_share": 100
    },
    "schedule": {
      "planned_start": "2026-11-03T07:00:00+00:00",
      "planned_end": "2026-11-03T11:00:00+00:00",
      "earliest_start": "2026-11-03T07:00:00+00:00",
      "latest_start": "2026-11-03T09:00:00+00:00",
      "estimated_minutes": 240,
      "actual_start": null,
      "actual_end": null,
      "actual_minutes": null
    },
    "assignments": [
      {
        "id": "01a15c11-0b1c-7f20-b162-93a4ebf10d45",
        "role": "lead",
        "status": "assigned",
        "notified_at": "2026-10-28T13:02:11+00:00",
        "accepted_at": null,
        "employee": {
          "id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01",
          "name": "Ali Hassan",
          "color": "#2F80ED"
        }
      }
    ],
    "services": [
      {
        "id": "01a15c14-4e5f-7253-a4b5-c6d71e243078",
        "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
        "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
        "service_id": "01a15b80-5a6b-7c8d-9eaf-b0c1d2e3f405",
        "name": "Flyttstädning",
        "quantity": 4,
        "unit_price_cents": 49500,
        "discount_percent": 0,
        "vat_rate": 25,
        "rot_rut_eligible": true,
        "sort_order": 0,
        "created_at": "2026-10-28T13:01:55.000000Z",
        "updated_at": "2026-10-28T13:01:55.000000Z"
      }
    ],
    "checklist": [
      {
        "id": "01a15c12-2c3d-7031-8273-a4b5fc021e56",
        "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
        "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
        "label": "Rengör ugn, fläkt och kylskåp",
        "is_done": false,
        "is_required": true,
        "done_by": null,
        "done_at": null,
        "sort_order": 0,
        "created_at": "2026-10-28T13:05:40.000000Z",
        "updated_at": "2026-10-28T13:05:40.000000Z"
      }
    ],
    "materials": [],
    "completed_at": null,
    "cancelled_at": null,
    "cancel_reason": null,
    "customer_signed_at": null,
    "created_at": "2026-10-28T13:01:55+00:00",
    "updated_at": "2026-10-28T13:02:11+00:00"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A job (uppdrag): what is to be done, for which customer, where and when, and by whom. Which nested parts are included depends on the endpoint — see each property.

      • idstring(uuid)required

        The work order id.

      • order_numberstringrequired

        Number from the organization's work order series, unique within the organization (format set in the panel, e.g. 1042 or WO-01042).

      • titlestringrequired

        What the job is, as shown on the board.

      • descriptionstringnullablerequired

        Free text for the people doing the job.

      • statusstringrequired

        booked: promised to the customer, nobody placed yet. scheduled: somebody is going. in_progress: they are there. completed: the work is done (and billable). invoiced: the money has been asked for. cancelled: not happening.

        One ofbookedscheduledin_progresscompletedinvoicedcancelled

      • priorityintegerrequired

        -2 (lowest) to 2 (highest); 0 is normal.

        -2–2

      • sourcestringrequired

        Where the job came from: manual (created in the panel or this API), recurrence (written by a recurrence) or quote (made from an accepted quote — see handover). More values may be added.

      • handoverobjectnullablerequired

        The accepted quote this work order was made from, or null when it was not.

      • customerobjectrequired

        The customer the job is for (a customer from the Customers API). Still shown after that customer is deleted: a deleted customer keeps its past jobs.

      • contactobjectnullable

        The customer contact for this job, or null. Included by GET /work-orders/{workOrder} only.

      • service_addressobjectnullable

        Where the work is done, or null. Included by the list, GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • pricingobjectrequired
      • scheduleobjectrequired

        When the job is planned, what was promised, and when it actually ran. All UTC.

      • assignmentsarray of objects

        Who is going. Included everywhere except the response to creating a work order.

      • servicesarray of objects

        Services ordered for the job, priced as they were when ordered. Included by GET /work-orders/{workOrder} only.

      • checklistarray of objects

        The steps to tick, in sort_order. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • materialsarray of objects

        Materials used. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete; GET /work-orders/{workOrder}/materials returns the same lines with net_cents.

      • completed_atstring(date-time)nullablerequired

        When the job was completed, or null.

      • cancelled_atstring(date-time)nullablerequired

        When the job was cancelled, or null.

      • cancel_reasonstringnullablerequired

        The reason given when cancelling, or null.

      • customer_signed_atstring(date-time)nullablerequired

        When the customer signed on completion, or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Update a work order

PATCH/work-orders/{workOrder}
Scope write Module Scheduling & work orders Permission booking.work_orders.manage

Changes the fields you send and leaves the rest. Sending null clears a nullable field.

What this does not do: change the status (use POST /work-orders/{workOrder}/status), or check the new times against anybody's calendar — moving a job that has people on it is POST /schedule/move, which warns about double bookings. Changing planned_start here never changes the status either.

curl -X PATCH 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.",
  "priority": 2,
  "agreed_price_cents": null
}'
Response · 200
{
  "data": {
    "id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "order_number": "1042",
    "title": "Flyttstädning – Storgatan 12, Uppsala",
    "description": "Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.",
    "status": "scheduled",
    "priority": 1,
    "source": "manual",
    "handover": null,
    "customer": {
      "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "name": "Brf Linden",
      "customer_number": "10231"
    },
    "pricing": {
      "model": "hourly",
      "agreed_price_cents": null,
      "currency": "SEK",
      "rot_rut": "rut",
      "rot_rut_share": 100
    },
    "schedule": {
      "planned_start": "2026-11-03T07:00:00+00:00",
      "planned_end": "2026-11-03T11:00:00+00:00",
      "earliest_start": "2026-11-03T07:00:00+00:00",
      "latest_start": "2026-11-03T09:00:00+00:00",
      "estimated_minutes": 240,
      "actual_start": null,
      "actual_end": null,
      "actual_minutes": null
    },
    "assignments": [
      {
        "id": "01a15c11-0b1c-7f20-b162-93a4ebf10d45",
        "role": "lead",
        "status": "assigned",
        "notified_at": "2026-10-28T13:02:11+00:00",
        "accepted_at": null,
        "employee": {
          "id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01",
          "name": "Ali Hassan",
          "color": "#2F80ED"
        }
      }
    ],
    "completed_at": null,
    "cancelled_at": null,
    "cancel_reason": null,
    "customer_signed_at": null,
    "created_at": "2026-10-28T13:01:55+00:00",
    "updated_at": "2026-10-28T13:02:11+00:00"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Request body

application/json · required

  • account_idstring(uuid)

    The customer (a customer id from the Customers API). Must be one of your organization's.

  • titlestring

    What the job is.

    at most 255 characters

  • contact_idstring(uuid)nullable

    A contact at the customer, or null.

  • service_address_idstring(uuid)nullable

    Where the work is done: one of the customer's addresses, or null.

  • descriptionstringnullable

    Free text for the people doing the job.

  • priorityinteger

    -2 to 2. Default 0.

    -2–2

  • price_modelstring

    How the customer is charged: by approved hours, a fixed agreed_price_cents, or per unit of the ordered services.

    One ofhourlyfixedper_unit

    Default "hourly"

  • agreed_price_centsintegernullable

    Agreed price for a fixed job, in öre excluding VAT.

    ≥ 0

  • rot_rutstring

    Swedish tax deduction on the labour: ROT (repairs, 30 %) or RUT (household services, 50 %), computed on the invoice basis.

    One ofnonerotrut

    Default "none"

  • rot_rut_sharenumbernullable

    Share of the deduction this customer claims, 0–100 percent.

    0–100

  • planned_startstring(date-time)nullable

    ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Null for unplanned work.

  • planned_endstring(date-time)nullable

    ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Must be after planned_start.

  • earliest_startstring(date-time)nullable

    Customer's window, earliest acceptable start. ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC.

  • latest_startstring(date-time)nullable

    Customer's window, latest acceptable start; not before earliest_start. ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC.

  • estimated_minutesintegernullable

    Expected duration in minutes.

    ≥ 1

  • sub_entity_idstring(uuid)nullable

    The office (sub-entity) the job belongs to, for organizations with several. Must be one of yours.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A job (uppdrag): what is to be done, for which customer, where and when, and by whom. Which nested parts are included depends on the endpoint — see each property.

      • idstring(uuid)required

        The work order id.

      • order_numberstringrequired

        Number from the organization's work order series, unique within the organization (format set in the panel, e.g. 1042 or WO-01042).

      • titlestringrequired

        What the job is, as shown on the board.

      • descriptionstringnullablerequired

        Free text for the people doing the job.

      • statusstringrequired

        booked: promised to the customer, nobody placed yet. scheduled: somebody is going. in_progress: they are there. completed: the work is done (and billable). invoiced: the money has been asked for. cancelled: not happening.

        One ofbookedscheduledin_progresscompletedinvoicedcancelled

      • priorityintegerrequired

        -2 (lowest) to 2 (highest); 0 is normal.

        -2–2

      • sourcestringrequired

        Where the job came from: manual (created in the panel or this API), recurrence (written by a recurrence) or quote (made from an accepted quote — see handover). More values may be added.

      • handoverobjectnullablerequired

        The accepted quote this work order was made from, or null when it was not.

      • customerobjectrequired

        The customer the job is for (a customer from the Customers API). Still shown after that customer is deleted: a deleted customer keeps its past jobs.

      • contactobjectnullable

        The customer contact for this job, or null. Included by GET /work-orders/{workOrder} only.

      • service_addressobjectnullable

        Where the work is done, or null. Included by the list, GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • pricingobjectrequired
      • scheduleobjectrequired

        When the job is planned, what was promised, and when it actually ran. All UTC.

      • assignmentsarray of objects

        Who is going. Included everywhere except the response to creating a work order.

      • servicesarray of objects

        Services ordered for the job, priced as they were when ordered. Included by GET /work-orders/{workOrder} only.

      • checklistarray of objects

        The steps to tick, in sort_order. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • materialsarray of objects

        Materials used. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete; GET /work-orders/{workOrder}/materials returns the same lines with net_cents.

      • completed_atstring(date-time)nullablerequired

        When the job was completed, or null.

      • cancelled_atstring(date-time)nullablerequired

        When the job was cancelled, or null.

      • cancel_reasonstringnullablerequired

        The reason given when cancelling, or null.

      • customer_signed_atstring(date-time)nullablerequired

        When the customer signed on completion, or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Delete a work order

DELETE/work-orders/{workOrder}
Scope write Module Scheduling & work orders Permission booking.work_orders.manage

Deletes the job: it disappears from every list and read, and answers 404 from then on. Prefer cancelling (POST /work-orders/{workOrder}/status with cancelled) for a job the customer called off — that keeps the record and the reason. A deleted job that a recurrence wrote is not written again for that date.

curl -X DELETE 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response · 200
{
  "deleted": true
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • deletedbooleanrequired

      One oftrue

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Change the status of a work order

POST/work-orders/{workOrder}/status
Scope write Module Scheduling & work orders Permission booking.work_orders.manage

Moves a job along its life. Only these transitions are allowed; anything else is a 409 and nothing changes:

FromMay become
bookedscheduled, cancelled
scheduledbooked, in_progress, cancelled
in_progresscompleted, cancelled
completedinvoiced
invoiced, cancelled— (final)

Side effects: in_progress stamps actual_start with the current time. cancelled stamps cancelled_at and stores reason as cancel_reason. completed is the same as POST /work-orders/{workOrder}/complete with an empty body: actual_end defaults to now and actual_start to what was recorded, completed_at is now and completed_by the member the key acts as, it is refused (409) while a required checklist step is open or when the job is already completed, and it fires the work_order.completed webhook and makes the job billable. Use /complete instead when you have the checklist, materials, signature or corrected times to record with it.

Set invoiced yourself when you bill a completed job outside Closepact (Fortnox, Visma, by hand); Closepact Invoicing sets it when it creates an invoice from the job. Assigning and unassigning move a job between booked and scheduled on their own.

curl -X POST 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/status' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "cancelled",
  "reason": "Kunden har sålt lägenheten och avbokat."
}'
Response
{
  "data": {
    "id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "order_number": "1042",
    "title": "Flyttstädning – Storgatan 12, Uppsala",
    "description": "Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.",
    "status": "cancelled",
    "priority": 1,
    "source": "manual",
    "handover": null,
    "customer": {
      "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "name": "Brf Linden",
      "customer_number": "10231"
    },
    "pricing": {
      "model": "hourly",
      "agreed_price_cents": null,
      "currency": "SEK",
      "rot_rut": "rut",
      "rot_rut_share": 100
    },
    "schedule": {
      "planned_start": "2026-11-03T07:00:00+00:00",
      "planned_end": "2026-11-03T11:00:00+00:00",
      "earliest_start": "2026-11-03T07:00:00+00:00",
      "latest_start": "2026-11-03T09:00:00+00:00",
      "estimated_minutes": 240,
      "actual_start": null,
      "actual_end": null,
      "actual_minutes": null
    },
    "assignments": [
      {
        "id": "01a15c11-0b1c-7f20-b162-93a4ebf10d45",
        "role": "lead",
        "status": "assigned",
        "notified_at": "2026-10-28T13:02:11+00:00",
        "accepted_at": null,
        "employee": {
          "id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01",
          "name": "Ali Hassan",
          "color": "#2F80ED"
        }
      }
    ],
    "completed_at": null,
    "cancelled_at": "2026-10-30T09:12:00+00:00",
    "cancel_reason": "Kunden har sålt lägenheten och avbokat.",
    "customer_signed_at": null,
    "created_at": "2026-10-28T13:01:55+00:00",
    "updated_at": "2026-10-30T09:12:00+00:00"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Request body

application/json · required

  • statusstringrequired

    booked: promised to the customer, nobody placed yet. scheduled: somebody is going. in_progress: they are there. completed: the work is done (and billable). invoiced: the money has been asked for. cancelled: not happening.

    One ofbookedscheduledin_progresscompletedinvoicedcancelled

  • reasonstringnullable

    Why, when cancelling. Stored as cancel_reason.

    at most 500 characters

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A job (uppdrag): what is to be done, for which customer, where and when, and by whom. Which nested parts are included depends on the endpoint — see each property.

      • idstring(uuid)required

        The work order id.

      • order_numberstringrequired

        Number from the organization's work order series, unique within the organization (format set in the panel, e.g. 1042 or WO-01042).

      • titlestringrequired

        What the job is, as shown on the board.

      • descriptionstringnullablerequired

        Free text for the people doing the job.

      • statusstringrequired

        booked: promised to the customer, nobody placed yet. scheduled: somebody is going. in_progress: they are there. completed: the work is done (and billable). invoiced: the money has been asked for. cancelled: not happening.

        One ofbookedscheduledin_progresscompletedinvoicedcancelled

      • priorityintegerrequired

        -2 (lowest) to 2 (highest); 0 is normal.

        -2–2

      • sourcestringrequired

        Where the job came from: manual (created in the panel or this API), recurrence (written by a recurrence) or quote (made from an accepted quote — see handover). More values may be added.

      • handoverobjectnullablerequired

        The accepted quote this work order was made from, or null when it was not.

      • customerobjectrequired

        The customer the job is for (a customer from the Customers API). Still shown after that customer is deleted: a deleted customer keeps its past jobs.

      • contactobjectnullable

        The customer contact for this job, or null. Included by GET /work-orders/{workOrder} only.

      • service_addressobjectnullable

        Where the work is done, or null. Included by the list, GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • pricingobjectrequired
      • scheduleobjectrequired

        When the job is planned, what was promised, and when it actually ran. All UTC.

      • assignmentsarray of objects

        Who is going. Included everywhere except the response to creating a work order.

      • servicesarray of objects

        Services ordered for the job, priced as they were when ordered. Included by GET /work-orders/{workOrder} only.

      • checklistarray of objects

        The steps to tick, in sort_order. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • materialsarray of objects

        Materials used. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete; GET /work-orders/{workOrder}/materials returns the same lines with net_cents.

      • completed_atstring(date-time)nullablerequired

        When the job was completed, or null.

      • cancelled_atstring(date-time)nullablerequired

        When the job was cancelled, or null.

      • cancel_reasonstringnullablerequired

        The reason given when cancelling, or null.

      • customer_signed_atstring(date-time)nullablerequired

        When the customer signed on completion, or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Complete a work order

POST/work-orders/{workOrder}/complete

Closes a job from the doorstep in one request: ticks checklist steps, adds the materials used, stores the customer's signature and note, corrects the times, and sets the status to completed. All of it is written or none of it is.

Only an in_progress job can be completed, and only once every required checklist step is done — ticks sent in this request count. A second call answers 409 instead of storing a second signature; if a retry after a timeout gets that 409, the first call went through.

Times: actual_end defaults to now and actual_start to what was recorded when the job started; send either to correct it. completed_at is now, and completed_by the member the key acts as.

What completing triggers: the work_order.completed webhook; the job becomes billable, so its invoice basis (GET /work-orders/{workOrder}/invoice-basis) settles and Closepact Invoicing can invoice it, which moves it to invoiced.

Needs only booking.read: completing is reporting what was done, and the field worker who does it holds nothing more on booking.

curl -X POST 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/complete' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "checklist": [
    {
      "id": "01a15c12-2c3d-7031-8273-a4b5fc021e56",
      "is_done": true
    }
  ],
  "materials": [
    {
      "name": "Ugnsrengöring, sprayflaska",
      "quantity": 2,
      "unit": "st",
      "unit_price_cents": 8900
    }
  ],
  "signature": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…",
  "signer_name": "Karin Ek",
  "note": "Klart. Nycklarna lämnade i nyckelskåpet.",
  "actual_end": "2026-11-03T12:12:00+01:00"
}'
Response
{
  "data": {
    "id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "order_number": "1042",
    "title": "Flyttstädning – Storgatan 12, Uppsala",
    "description": "Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.",
    "status": "completed",
    "priority": 1,
    "source": "manual",
    "handover": null,
    "customer": {
      "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "name": "Brf Linden",
      "customer_number": "10231"
    },
    "service_address": {
      "id": "01a15c02-4e1c-7d5e-9f40-7182c9daeb23",
      "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
      "account_id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "type": "service",
      "label": "Lägenhet 1102",
      "street": "Storgatan 12",
      "street2": "Lgh 1102, 3 tr",
      "zip": "753 20",
      "city": "Uppsala",
      "country": "SE",
      "lat": 59.8586,
      "lng": 17.6389,
      "geofence_radius_m": 150,
      "access_notes": "Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.",
      "contact_id": "01a15c02-4e1b-7c4d-8e3f-6071b8c9da12",
      "is_default": true,
      "metadata": [],
      "created_at": "2026-09-02T09:15:00.000000Z",
      "updated_at": "2026-09-02T09:15:00.000000Z",
      "deleted_at": null
    },
    "pricing": {
      "model": "hourly",
      "agreed_price_cents": null,
      "currency": "SEK",
      "rot_rut": "rut",
      "rot_rut_share": 100
    },
    "schedule": {
      "planned_start": "2026-11-03T07:00:00+00:00",
      "planned_end": "2026-11-03T11:00:00+00:00",
      "earliest_start": "2026-11-03T07:00:00+00:00",
      "latest_start": "2026-11-03T09:00:00+00:00",
      "estimated_minutes": 240,
      "actual_start": "2026-11-03T07:04:00+00:00",
      "actual_end": "2026-11-03T11:12:00+00:00",
      "actual_minutes": 248
    },
    "assignments": [
      {
        "id": "01a15c11-0b1c-7f20-b162-93a4ebf10d45",
        "role": "lead",
        "status": "assigned",
        "notified_at": "2026-10-28T13:02:11+00:00",
        "accepted_at": null,
        "employee": {
          "id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01",
          "name": "Ali Hassan",
          "color": "#2F80ED"
        }
      }
    ],
    "checklist": [
      {
        "id": "01a15c12-2c3d-7031-8273-a4b5fc021e56",
        "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
        "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
        "label": "Rengör ugn, fläkt och kylskåp",
        "is_done": true,
        "is_required": true,
        "done_by": "01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091",
        "done_at": "2026-11-03T10:41:27.000000Z",
        "sort_order": 0,
        "created_at": "2026-10-28T13:05:40.000000Z",
        "updated_at": "2026-11-03T10:41:27.000000Z"
      }
    ],
    "materials": [
      {
        "id": "01a15c13-3d4e-7142-9384-b5c60d132f67",
        "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
        "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
        "name": "Ugnsrengöring, sprayflaska",
        "quantity": 2,
        "unit": "st",
        "unit_price_cents": 8900,
        "discount_percent": 0,
        "vat_rate": 25,
        "rot_rut_eligible": false,
        "added_by": "01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091",
        "created_at": "2026-11-03T10:52:03.000000Z",
        "updated_at": "2026-11-03T10:52:03.000000Z"
      }
    ],
    "completed_at": "2026-11-03T11:12:40+00:00",
    "cancelled_at": null,
    "cancel_reason": null,
    "customer_signed_at": "2026-11-03T11:12:40+00:00",
    "created_at": "2026-10-28T13:01:55+00:00",
    "updated_at": "2026-11-03T11:12:40+00:00"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Request body

application/json · required

  • checklistarray of objects

    Ticks to record before completing. Every id must be a step on this job (422 otherwise).

  • materialsarray of objects

    Material lines to add (added to any already on the job).

  • signaturestringnullable

    The customer's signature: a PNG or JPEG image, base64-encoded, with or without a data:image/png;base64, prefix. Optional — the customer is not always home.

  • signer_namestring

    Name of the person who signed. Required with signature.

    at most 160 characters

  • notestringnullable

    What the worker wants the office to know.

    at most 2,000 characters

  • actual_startstring(date-time)nullable

    Correct when the work started. ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC.

  • actual_endstring(date-time)nullable

    When the work ended; default now. Not before the start (409). ISO 8601 date-time. An offset is honoured (2026-11-03T08:00:00+01:00 is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A job (uppdrag): what is to be done, for which customer, where and when, and by whom. Which nested parts are included depends on the endpoint — see each property.

      • idstring(uuid)required

        The work order id.

      • order_numberstringrequired

        Number from the organization's work order series, unique within the organization (format set in the panel, e.g. 1042 or WO-01042).

      • titlestringrequired

        What the job is, as shown on the board.

      • descriptionstringnullablerequired

        Free text for the people doing the job.

      • statusstringrequired

        booked: promised to the customer, nobody placed yet. scheduled: somebody is going. in_progress: they are there. completed: the work is done (and billable). invoiced: the money has been asked for. cancelled: not happening.

        One ofbookedscheduledin_progresscompletedinvoicedcancelled

      • priorityintegerrequired

        -2 (lowest) to 2 (highest); 0 is normal.

        -2–2

      • sourcestringrequired

        Where the job came from: manual (created in the panel or this API), recurrence (written by a recurrence) or quote (made from an accepted quote — see handover). More values may be added.

      • handoverobjectnullablerequired

        The accepted quote this work order was made from, or null when it was not.

      • customerobjectrequired

        The customer the job is for (a customer from the Customers API). Still shown after that customer is deleted: a deleted customer keeps its past jobs.

      • contactobjectnullable

        The customer contact for this job, or null. Included by GET /work-orders/{workOrder} only.

      • service_addressobjectnullable

        Where the work is done, or null. Included by the list, GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • pricingobjectrequired
      • scheduleobjectrequired

        When the job is planned, what was promised, and when it actually ran. All UTC.

      • assignmentsarray of objects

        Who is going. Included everywhere except the response to creating a work order.

      • servicesarray of objects

        Services ordered for the job, priced as they were when ordered. Included by GET /work-orders/{workOrder} only.

      • checklistarray of objects

        The steps to tick, in sort_order. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete.

      • materialsarray of objects

        Materials used. Included by GET /work-orders/{workOrder} and POST /work-orders/{workOrder}/complete; GET /work-orders/{workOrder}/materials returns the same lines with net_cents.

      • completed_atstring(date-time)nullablerequired

        When the job was completed, or null.

      • cancelled_atstring(date-time)nullablerequired

        When the job was cancelled, or null.

      • cancel_reasonstringnullablerequired

        The reason given when cancelling, or null.

      • customer_signed_atstring(date-time)nullablerequired

        When the customer signed on completion, or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Get the invoice basis of a work order

GET/work-orders/{workOrder}/invoice-basis

What the job comes to, stated so that it can be invoiced in Closepact or elsewhere (Fortnox, Visma).

The price model decides what is billed: an hourly job bills the approved hours (at the single hourly price of the ordered services, or else each person's recorded rate); a fixed job bills agreed_price_cents and lists the hours as unbilled (with no agreed price it bills the hours and warns fixed_price_missing); a per_unit job bills the ordered services per unit and lists the hours as unbilled. Approved hours carry the employee's cost rate unless the ordered services name one price — a rate of 0 is billed as 0 and warned as labour_rate_missing. Materials are always their own lines. Only approved time counts; time still waiting for approval is reported under time and in warnings.

Available in any status. Until the job is completed it will still change (warning not_completed); an invoiced job shows what was billed (already_invoiced).

curl 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/invoice-basis' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response · 200
{
  "data": {
    "work_order": {
      "id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
      "order_number": "1042",
      "title": "Flyttstädning – Storgatan 12, Uppsala",
      "status": "completed",
      "price_model": "hourly",
      "currency": "SEK",
      "agreed_price_cents": null,
      "completed_at": "2026-11-03T11:12:40+00:00",
      "planned_start": "2026-11-03T07:00:00+00:00",
      "account": {
        "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
        "name": "Brf Linden",
        "customer_number": "10231"
      }
    },
    "already_invoiced": false,
    "lines": [
      {
        "kind": "labour",
        "description": "Ali Hassan",
        "detail": "work",
        "quantity": 3.883,
        "unit_price_cents": 49500,
        "discount_percent": 0,
        "net_cents": 192225,
        "vat_rate": 25,
        "vat_cents": 48056,
        "rot_rut_eligible": true,
        "billed": true,
        "excluded_reason": null,
        "source": {
          "type": "time_entries",
          "employee_id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01",
          "minutes": 233,
          "entries": 1,
          "rate_basis": "employee_rate",
          "cost_cents": 124267
        }
      },
      {
        "kind": "material",
        "description": "Ugnsrengöring, sprayflaska",
        "detail": "st",
        "quantity": 2,
        "unit_price_cents": 8900,
        "discount_percent": 0,
        "net_cents": 17800,
        "vat_rate": 25,
        "vat_cents": 4450,
        "rot_rut_eligible": false,
        "billed": true,
        "excluded_reason": null,
        "source": {
          "type": "material",
          "work_order_material_id": "01a15c13-3d4e-7142-9384-b5c60d132f67"
        }
      }
    ],
    "totals": {
      "labour_net_cents": 192225,
      "labour_vat_cents": 48056,
      "materials_net_cents": 17800,
      "materials_vat_cents": 4450,
      "net_cents": 210025,
      "vat_cents": 52506,
      "gross_cents": 262531,
      "labour_cost_cents": 124267
    },
    "rot_rut": {
      "type": "rut",
      "rate_percent": 50,
      "share_percent": 100,
      "eligible_labour_net_cents": 192225,
      "eligible_labour_gross_cents": 240281,
      "claim_base_cents": 240281,
      "deduction_cents": 120141,
      "customer_pays_cents": 142390,
      "annual_ceiling_applied": false
    },
    "time": {
      "approved_minutes": 233,
      "awaiting_approval_minutes": 0,
      "awaiting_approval_entries": 0
    },
    "warnings": []
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      What a job is worth, line by line, so that it can be invoiced here or elsewhere. Only approved time is billed; labour and materials are separate lines; each line is rounded once and the totals are sums of the rounded lines.

      • work_orderobjectrequired
      • already_invoicedbooleanrequired

        True when the job is invoiced: the basis then shows what was billed.

      • linesarray of objectsrequired
      • totalsobjectrequired

        Sums over the billed lines, in öre. Labour includes a fixed price. labour_cost_cents is the exception: what the approved hours cost, for margin.

      • rot_rutobjectrequired

        The ROT/RUT deduction, computed on the eligible labour including VAT. The buyer's annual ceiling is not applied (annual_ceiling_applied is always false): it depends on what other companies have claimed for them this year.

      • timeobjectrequired
      • warningsarray of objectsrequired
  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

List the checklist of a work order

GET/work-orders/{workOrder}/checklist

The steps on a job, ordered by id (creation order). Show them by sort_order, which is what the field worker sees. Cursor-paginated: pass next_cursor as cursor.

curl 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/checklist' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "01a15c12-2c3d-7031-8273-a4b5fc021e56",
      "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
      "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
      "label": "Rengör ugn, fläkt och kylskåp",
      "is_done": false,
      "is_required": true,
      "done_by": null,
      "done_at": null,
      "sort_order": 0,
      "created_at": "2026-10-28T13:05:40.000000Z",
      "updated_at": "2026-10-28T13:05:40.000000Z"
    }
  ],
  "path": "https://api.closepact.com/v1/api/work-orders/01a15c10-8a2b-7e6f-a051-8293dae0fc34/checklist",
  "per_page": 100,
  "next_cursor": null,
  "next_page_url": null,
  "prev_cursor": null,
  "prev_page_url": null
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Query parameters

  • limitinteger

    Items per page, 1–200.

    Default 1001–200

  • cursorstring

    Opaque cursor from meta.next_cursor or meta.prev_cursor of the previous page.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.

    A page of a cursor-paginated list. Pass next_cursor as cursor to fetch the next page; it is null on the last page.

    • dataarray of objectsrequired
      • idstring(uuid)required

        The checklist item id.

      • organization_idstring(uuid)required

        Your organization.

      • work_order_idstring(uuid)required

        The work order it belongs to.

      • labelstringrequired

        What to do.

      • is_donebooleanrequired

        Whether it has been ticked.

      • is_requiredbooleanrequired

        Whether the job can be completed without it.

      • done_bystring(uuid)nullablerequired

        The member who ticked it (for an API call, the member the key acts as), or null.

      • done_atstring(date-time)nullablerequired

        When it was ticked, or null. Set by Closepact when is_done changes; never accepted from a request.

      • sort_orderintegerrequired

        Position in the list, from 0.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

    • pathstringrequired

      The list URL without query.

    • per_pageintegerrequired

      Items per page.

    • next_cursorstringnullablerequired

      Cursor for the next page, or null.

    • next_page_urlstringnullablerequired

      URL of the next page, or null.

    • prev_cursorstringnullablerequired

      Cursor for the previous page, or null.

    • prev_page_urlstringnullablerequired

      URL of the previous page, or null.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Add a checklist item

POST/work-orders/{workOrder}/checklist
Scope write Module Scheduling & work orders Permission booking.work_orders.manage

Adds a step to a job — at the end of the list unless you give sort_order. A required step (is_required: true) must be ticked before the job can be completed. The checklist of a completed or invoiced job cannot change (409).

curl -X POST 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/checklist' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "Rengör ugn, fläkt och kylskåp",
  "is_required": true
}'
Response
{
  "data": {
    "id": "01a15c12-2c3d-7031-8273-a4b5fc021e56",
    "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
    "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "label": "Rengör ugn, fläkt och kylskåp",
    "is_done": false,
    "is_required": true,
    "done_by": null,
    "done_at": null,
    "sort_order": 0,
    "created_at": "2026-10-28T13:05:40.000000Z",
    "updated_at": "2026-10-28T13:05:40.000000Z"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Request body

application/json · required

  • labelstringrequired

    What to do.

    at most 255 characters

  • is_requiredboolean

    Whether the job can be completed without it. Default false.

  • sort_orderinteger

    Position, from 0. Default: after the last item.

    ≥ 0

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A step on a job. A job cannot be completed while a required step is not done.

      • idstring(uuid)required

        The checklist item id.

      • organization_idstring(uuid)required

        Your organization.

      • work_order_idstring(uuid)required

        The work order it belongs to.

      • labelstringrequired

        What to do.

      • is_donebooleanrequired

        Whether it has been ticked.

      • is_requiredbooleanrequired

        Whether the job can be completed without it.

      • done_bystring(uuid)nullablerequired

        The member who ticked it (for an API call, the member the key acts as), or null.

      • done_atstring(date-time)nullablerequired

        When it was ticked, or null. Set by Closepact when is_done changes; never accepted from a request.

      • sort_orderintegerrequired

        Position in the list, from 0.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Tick or change a checklist item

PATCH/work-orders/{workOrder}/checklist/{item}

Ticks or unticks a step, renames it, makes it required or moves it. Ticking stamps done_by (the member the key acts as) and done_at; unticking clears them; re-sending the same is_done leaves them alone. done_by and done_at are never taken from the request.

Needs only booking.read, so a field worker's key can tick. The checklist of a completed or invoiced job cannot change (409). An item of another job is a 404.

curl -X PATCH 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/checklist/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "is_done": true
}'
Response
{
  "data": {
    "id": "01a15c12-2c3d-7031-8273-a4b5fc021e56",
    "organization_id": "01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21",
    "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "label": "Rengör ugn, fläkt och kylskåp",
    "is_done": true,
    "is_required": true,
    "done_by": "01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091",
    "done_at": "2026-11-03T10:41:27.000000Z",
    "sort_order": 0,
    "created_at": "2026-10-28T13:05:40.000000Z",
    "updated_at": "2026-11-03T10:41:27.000000Z"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

  • itemstring(uuid)required

    The checklist item id.

Request body

application/json · required

  • labelstring

    What to do.

    at most 255 characters

  • is_requiredboolean

    Whether the job can be completed without it. Default false.

  • sort_orderinteger

    Position, from 0. Default: after the last item.

    ≥ 0

  • is_doneboolean

    Ticked or not.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A step on a job. A job cannot be completed while a required step is not done.

      • idstring(uuid)required

        The checklist item id.

      • organization_idstring(uuid)required

        Your organization.

      • work_order_idstring(uuid)required

        The work order it belongs to.

      • labelstringrequired

        What to do.

      • is_donebooleanrequired

        Whether it has been ticked.

      • is_requiredbooleanrequired

        Whether the job can be completed without it.

      • done_bystring(uuid)nullablerequired

        The member who ticked it (for an API call, the member the key acts as), or null.

      • done_atstring(date-time)nullablerequired

        When it was ticked, or null. Set by Closepact when is_done changes; never accepted from a request.

      • sort_orderintegerrequired

        Position in the list, from 0.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Remove a checklist item

DELETE/work-orders/{workOrder}/checklist/{item}
Scope write Module Scheduling & work orders Permission booking.work_orders.manage

Removes a step. Not possible once the job is completed or invoiced (409).

curl -X DELETE 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/checklist/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response
{
  "deleted": true
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

  • itemstring(uuid)required

    The checklist item id.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • deletedbooleanrequired

      One oftrue

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

List the materials of a work order

GET/work-orders/{workOrder}/materials

Material lines on a job, oldest first, each with its net_cents. Cursor-paginated: pass next_cursor as cursor.

curl 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/materials' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "01a15c13-3d4e-7142-9384-b5c60d132f67",
      "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
      "name": "Ugnsrengöring, sprayflaska",
      "quantity": 2,
      "unit": "st",
      "unit_price_cents": 8900,
      "discount_percent": 0,
      "vat_rate": 25,
      "rot_rut_eligible": false,
      "net_cents": 17800,
      "added_by": "01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091",
      "created_at": "2026-11-03T10:52:03+00:00",
      "updated_at": "2026-11-03T10:52:03+00:00"
    }
  ],
  "path": "https://api.closepact.com/v1/api/work-orders/01a15c10-8a2b-7e6f-a051-8293dae0fc34/materials",
  "per_page": 100,
  "next_cursor": null,
  "next_page_url": null,
  "prev_cursor": null,
  "prev_page_url": null
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Query parameters

  • limitinteger

    Items per page, 1–200.

    Default 1001–200

  • cursorstring

    Opaque cursor from meta.next_cursor or meta.prev_cursor of the previous page.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.

    A page of a cursor-paginated list. Pass next_cursor as cursor to fetch the next page; it is null on the last page.

    • dataarray of objectsrequired
      • idstring(uuid)required

        The material line id.

      • work_order_idstring(uuid)required

        The work order it belongs to.

      • namestringrequired

        What was used.

      • quantitynumberrequired

        How much, up to three decimals (2.5 metres of cable).

      • unitstringnullablerequired

        Unit as written, e.g. st, m, l, or null.

      • unit_price_centsintegerrequired

        Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit.

      • discount_percentnumberrequired

        Discount on the line in percent, up to two decimals. 0 unless the line was carried over from an accepted quote.

        0–100

      • vat_ratenumberrequired

        VAT in percent.

      • rot_rut_eligiblebooleanrequired

        Whether the line counts toward ROT/RUT. False unless set: the deduction applies to labour, and the invoice basis never applies it to materials.

      • net_centsintegerrequired

        quantity × unit_price_cents less discount_percent, rounded once to whole öre, excluding VAT.

      • added_bystring(uuid)nullablerequired

        The member who added the line (for an API call, the member the key acts as), or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

    • pathstringrequired

      The list URL without query.

    • per_pageintegerrequired

      Items per page.

    • next_cursorstringnullablerequired

      Cursor for the next page, or null.

    • next_page_urlstringnullablerequired

      URL of the next page, or null.

    • prev_cursorstringnullablerequired

      Cursor for the previous page, or null.

    • prev_page_urlstringnullablerequired

      URL of the previous page, or null.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Add a material line

POST/work-orders/{workOrder}/materials

Records something used on the job. It appears on the invoice basis as its own line. added_by is the member the key acts as.

Allowed until the job is invoiced (409 after that) — including after completion, when the office adds what the van used. Needs only booking.read, so a field worker's key can report materials.

curl -X POST 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/materials' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Ugnsrengöring, sprayflaska",
  "quantity": 2,
  "unit": "st",
  "unit_price_cents": 8900,
  "vat_rate": 25
}'
Response
{
  "data": {
    "id": "01a15c13-3d4e-7142-9384-b5c60d132f67",
    "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "name": "Ugnsrengöring, sprayflaska",
    "quantity": 2,
    "unit": "st",
    "unit_price_cents": 8900,
    "discount_percent": 0,
    "vat_rate": 25,
    "rot_rut_eligible": false,
    "net_cents": 17800,
    "added_by": "01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091",
    "created_at": "2026-11-03T10:52:03+00:00",
    "updated_at": "2026-11-03T10:52:03+00:00"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

Request body

application/json · required

  • namestringrequired

    What was used.

    at most 160 characters

  • quantitynumber

    How much, up to three decimals. Default 1.

    ≥ 0

  • unitstringnullable

    Unit, e.g. st, m, l.

    at most 32 characters

  • unit_price_centsinteger

    Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit. Default 0.

    ≥ 0

  • vat_ratenumber

    VAT in percent. Default 25.

    0–100

  • rot_rut_eligibleboolean

    Whether the line counts toward ROT/RUT. Default false — the deduction is for labour.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A line of material used on a job, as the invoice will read it.

      • idstring(uuid)required

        The material line id.

      • work_order_idstring(uuid)required

        The work order it belongs to.

      • namestringrequired

        What was used.

      • quantitynumberrequired

        How much, up to three decimals (2.5 metres of cable).

      • unitstringnullablerequired

        Unit as written, e.g. st, m, l, or null.

      • unit_price_centsintegerrequired

        Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit.

      • discount_percentnumberrequired

        Discount on the line in percent, up to two decimals. 0 unless the line was carried over from an accepted quote.

        0–100

      • vat_ratenumberrequired

        VAT in percent.

      • rot_rut_eligiblebooleanrequired

        Whether the line counts toward ROT/RUT. False unless set: the deduction applies to labour, and the invoice basis never applies it to materials.

      • net_centsintegerrequired

        quantity × unit_price_cents less discount_percent, rounded once to whole öre, excluding VAT.

      • added_bystring(uuid)nullablerequired

        The member who added the line (for an API call, the member the key acts as), or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Change a material line

PATCH/work-orders/{workOrder}/materials/{material}

Changes the fields you send. Not possible once the job is invoiced (409). A line of another job is a 404.

curl -X PATCH 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/materials/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "quantity": 3
}'
Response
{
  "data": {
    "id": "01a15c13-3d4e-7142-9384-b5c60d132f67",
    "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
    "name": "Ugnsrengöring, sprayflaska",
    "quantity": 3,
    "unit": "st",
    "unit_price_cents": 8900,
    "discount_percent": 0,
    "vat_rate": 25,
    "rot_rut_eligible": false,
    "net_cents": 26700,
    "added_by": "01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091",
    "created_at": "2026-11-03T10:52:03+00:00",
    "updated_at": "2026-11-03T13:20:00+00:00"
  }
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

  • materialstring(uuid)required

    The material line id.

Request body

application/json · required

  • namestring

    What was used.

    at most 160 characters

  • quantitynumber

    How much, up to three decimals. Default 1.

    ≥ 0

  • unitstringnullable

    Unit, e.g. st, m, l.

    at most 32 characters

  • unit_price_centsinteger

    Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit. Default 0.

    ≥ 0

  • vat_ratenumber

    VAT in percent. Default 25.

    0–100

  • rot_rut_eligibleboolean

    Whether the line counts toward ROT/RUT. Default false — the deduction is for labour.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • dataobjectrequired

      A line of material used on a job, as the invoice will read it.

      • idstring(uuid)required

        The material line id.

      • work_order_idstring(uuid)required

        The work order it belongs to.

      • namestringrequired

        What was used.

      • quantitynumberrequired

        How much, up to three decimals (2.5 metres of cable).

      • unitstringnullablerequired

        Unit as written, e.g. st, m, l, or null.

      • unit_price_centsintegerrequired

        Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit.

      • discount_percentnumberrequired

        Discount on the line in percent, up to two decimals. 0 unless the line was carried over from an accepted quote.

        0–100

      • vat_ratenumberrequired

        VAT in percent.

      • rot_rut_eligiblebooleanrequired

        Whether the line counts toward ROT/RUT. False unless set: the deduction applies to labour, and the invoice basis never applies it to materials.

      • net_centsintegerrequired

        quantity × unit_price_cents less discount_percent, rounded once to whole öre, excluding VAT.

      • added_bystring(uuid)nullablerequired

        The member who added the line (for an API call, the member the key acts as), or null.

      • created_atstring(date-time)required

        ISO 8601 date-time, in UTC.

      • updated_atstring(date-time)required

        ISO 8601 date-time, in UTC.

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 422The request body or query failed validation.
    Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors

Remove a material line

DELETE/work-orders/{workOrder}/materials/{material}
Scope write Module Scheduling & work orders Permission booking.work_orders.manage

Removes a line. Needs booking.work_orders.manage (adding is reporting; removing a cost line is a decision). Not possible once the job is invoiced (409).

curl -X DELETE 'https://api.closepact.com/v1/api/work-orders/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40/materials/01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response
{
  "deleted": true
}

Path parameters

  • workOrderstring(uuid)required

    The work order id.

  • materialstring(uuid)required

    The material line id.

Responses

  • Headers

    • X-RateLimit-Limit — Requests this key may make per minute.
    • X-RateLimit-Remaining — Requests left in the current minute.
    • deletedbooleanrequired

      One oftrue

  • 401No key, or the key is invalid, revoked or expired, or its creator no longer has access.
    Errors
  • 402The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: feature names the capability (the operation's x-feature) and required_tier the cheapest tier that grants it. Every operation answers this with "feature": "core.api" when no module the organization holds is on a tier that includes the API.
    Errors
  • 403The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (module_seat_required — an administrator assigns seats under Settings → Users), or the organization is suspended.
    Errors
  • 404No such record in this organization.
    Errors
  • Errors
  • 429The key has made more requests this minute than its limit allows.
    Errors