Schedule

Placing work: assign employees to jobs, move jobs in time, and find double bookings. Every placement is checked against the employee's other jobs, absences, working hours and employment.

Assign an employee to a work order

POST/schedule/assign

Sends an employee to a job, and optionally places the job in time in the same request. This is how an integration schedules work.

Before writing, the employee is checked at the job's (new) times against their other jobs, absences, availability rules and employment dates:

  • Errors — a double booking (another non-cancelled job of theirs overlaps; back-to-back is fine) or a date outside their employment — refuse the request with 409 schedule_conflict and the findings. Nothing is written. Send the same request with force: true to book anyway.
  • Warnings — outside their availability rules, or during an absence — never block. The assignment is written and the warnings come back in conflicts.

A job with no times is not checked: assigning somebody to unplanned work reserves them before the day is agreed.

Effects: a booked job that has a start moves to scheduled. Assigning the same employee again updates their assignment (role) instead of adding a second. A new assignment notifies the employee by email — and by SMS if the organization has turned that on — and sets notified_at. A work order or employee that is not your organization's is a 404.

curl -X POST 'https://api.closepact.com/v1/api/schedule/assign' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
  "employee_id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01",
  "role": "lead",
  "planned_start": "2026-11-03T08:00:00+01:00",
  "planned_end": "2026-11-03T12:00: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": "scheduled",
    "priority": 1,
    "source": "manual",
    "handover": null,
    "customer": {
      "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "name": "Brf Linden",
      "customer_number": 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"
  },
  "conflicts": [
    {
      "code": "outside_working_hours",
      "severity": "warning",
      "message": "Ali Hassan does not normally work those hours.",
      "context": {
        "hours": [
          {
            "start": "07:00:00",
            "end": "11:30:00"
          },
          {
            "start": "12:15:00",
            "end": "16:00:00"
          }
        ]
      }
    }
  ],
  "assignment_id": "01a15c11-0b1c-7f20-b162-93a4ebf10d45"
}

Request body

application/json · required

  • work_order_idstring(uuid)required

    The work order.

  • employee_idstring(uuid)required

    The employee to send.

  • rolestring

    Default lead.

    One ofleadhelper

  • planned_startstring(date-time)nullable

    Place the job at this time as well (otherwise its current times are used). 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.

  • planned_endstring(date-time)nullable

    End of that time; after planned_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.

  • forceboolean

    Write even if the check finds errors. Default false.

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.

    • conflictsarray of objectsrequired

      Warnings (and, with force, errors) that were overridden. Empty when the slot is clean.

    • assignment_idstring(uuid)required

      The assignment, for unassigning later.

  • 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

Move a work order to a new time

POST/schedule/move

Moves a job to new planned times, keeping everybody on it. Each assigned employee is checked at the new times exactly as when assigning: errors refuse the move with 409 schedule_conflict unless force is true, warnings come back in conflicts. The status does not change. A work order that is not your organization's is a 404.

curl -X POST 'https://api.closepact.com/v1/api/schedule/move' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
  "planned_start": "2026-11-04T08:00:00+01:00",
  "planned_end": "2026-11-04T12:00: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": "scheduled",
    "priority": 1,
    "source": "manual",
    "handover": null,
    "customer": {
      "id": "01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901",
      "name": "Brf Linden",
      "customer_number": null
    },
    "pricing": {
      "model": "hourly",
      "agreed_price_cents": null,
      "currency": "SEK",
      "rot_rut": "rut",
      "rot_rut_share": 100
    },
    "schedule": {
      "planned_start": "2026-11-04T07:00:00+00:00",
      "planned_end": "2026-11-04T11: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"
  },
  "conflicts": []
}

Request body

application/json · required

  • work_order_idstring(uuid)required

    The work order.

  • planned_startstring(date-time)required

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

  • planned_endstring(date-time)required

    New end; after planned_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.

  • forceboolean

    Write even if the check finds errors. Default false.

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.

    • conflictsarray of objectsrequired

      Findings for the people on the job at the new time.

  • 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

List conflicts on the schedule

GET/schedule/conflicts

Every problem on the board in a window: each planned job that overlaps the window and is not cancelled, completed or invoiced is checked for each employee on it, exactly as when assigning. Read-only — use it for a morning check or to show a warning banner. A double booking between two jobs is reported once from each side.

curl 'https://api.closepact.com/v1/api/schedule/conflicts?from=2026-11-02T00%3A00%3A00Z&to=2026-11-09T00%3A00%3A00Z' \
  -H "Authorization: Bearer $CLOSEPACT_API_KEY"
Response · 200
{
  "data": [
    {
      "code": "double_booked",
      "severity": "error",
      "message": "Ali Hassan is already on 1042 then.",
      "context": {
        "work_order_id": "01a15c10-8a2c-7f70-b162-93a4ebf10d45",
        "order_number": "1043",
        "title": "Fönsterputs – Kungsgatan 4, Uppsala",
        "planned_start": "2026-11-03T08:00:00+00:00",
        "planned_end": "2026-11-03T09:00:00+00:00"
      },
      "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
      "order_number": "1042",
      "employee_id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01"
    },
    {
      "code": "double_booked",
      "severity": "error",
      "message": "Ali Hassan is already on 1042 then.",
      "context": {
        "work_order_id": "01a15c10-8a2b-7e6f-a051-8293dae0fc34",
        "order_number": "1042",
        "title": "Flyttstädning – Storgatan 12, Uppsala",
        "planned_start": "2026-11-03T07:00:00+00:00",
        "planned_end": "2026-11-03T11:00:00+00:00"
      },
      "work_order_id": "01a15c10-8a2c-7f70-b162-93a4ebf10d45",
      "order_number": "1043",
      "employee_id": "01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01"
    }
  ]
}

Query parameters

  • fromstring(date-time)required

    Window start. ISO 8601 date or date-time; a date means midnight UTC.

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

  • tostring(date-time)required

    Window end, after from. ISO 8601 date or date-time.

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

Responses

  • Headers

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

        double_booked (error): already on another non-cancelled job that overlaps; jobs that only touch (one ends 12:00, the next starts 12:00) do not. not_employed (error): inactive, or outside started_at–ended_at (both days included). absent (warning): an absence that is not rejected overlaps. outside_working_hours (warning): the employee has availability rules and the job falls outside them. All but double_booked are judged on the Swedish clock: the job's UTC times are converted to Europe/Stockholm before they are compared with working hours, absences and employment dates.

        One ofdouble_bookednot_employedabsentoutside_working_hours

      • severitystringrequired

        Whether it blocks.

        One oferrorwarning

      • messagestringrequired

        Explanation, in English, naming the employee.

      • contextobjectrequired

        Details by code. double_booked: work_order_id, order_number, title, planned_start, planned_end of the other job. absent: absence_id, type, starts_at, ends_at. outside_working_hours: weekday (no hours that day) or hours (the day's start/end pairs). not_employed: status, started_at, ended_at.

      • work_order_idstring(uuid)required

        The job checked.

      • order_numberstringrequired

        Its number.

      • employee_idstring(uuid)required

        The employee checked.

  • 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

Take an employee off a work order

DELETE/work-orders/{workOrder}/assignments/{assignment}

Removes one assignment. A scheduled job left with nobody on it falls back to booked, so it shows up again among the work that needs placing. The employee is not notified. An assignment of another job is a 404.

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

Path parameters

  • workOrderstring(uuid)required

    The work order id.

  • assignmentstring(uuid)required

    The assignment id, as assignments[].id on the work order or assignment_id from POST /schedule/assign.

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