Skip to main content
Staffify Docs
Updated
Docs/Workspace API/Work orders

Work orders

A work order represents a field-service job: a technician visiting a customer to perform a service, breakdown repair, scheduled maintenance, installation, quote or warranty task. Scopes: operations.workorders.read / operations.workorders.write / operations.workorders.delete.

Field Service module required. These endpoints require the Field Service module to be enabled on your workspace. Calls return 403 MODULE_VAKMAN_DISABLED otherwise, even when the API key carries the right scopes.

Endpoints

GET/api/workspace/v1/workorders
GET/api/workspace/v1/workorders/{id}
POST/api/workspace/v1/workorders
PATCH/api/workspace/v1/workorders/{id}
POST/api/workspace/v1/workorders/{id}/status
DELETE/api/workspace/v1/workorders/{id}
POST/api/workspace/v1/workorders/{id}/restore

The work order object

FieldTypeDescription
workorder_numberstringAuto-generated, e.g. WO-2026-00001
statusenumopen | toegewezen | onderweg | uitgevoerd | gefactureerd | geannuleerd
workorder_typeenumservice | storing | onderhoud | montage | offerte | garantie (default service)
priorityenumlow | normal | high | urgent (default normal)
crm_contact_idintegerPrimary customer contact
assigned_team_member_idintegerTechnician assigned to the job
scheduled_atstring(ISO)When the visit is planned
duration_minutesintegerExpected job duration in minutes (default 60)
time_window_minutesintegerCustomer-facing arrival window (e.g. 120 = "between 10-12")
service_addressstringWork address
service_postcodestringWork address postcode
service_citystringWork address city
descriptionstringCustomer-reported issue
internal_notesstringTechnician notes, not shown to customer
customer_notesstringNotes shown to customer on completion
reported_atstring(ISO)When the issue was reported (SLA reaction time tracking)
installation_idintegerLinks to a specific installation under a maintenance contract
object_namestringBuilding/complex name (e.g. "HOA Lange Vijverberg 12")
buyer_referencestringCustomer PO number — forwarded to invoice BuyerReference (Peppol)
billing_contact_idintegerBilling contact override — invoice goes to this contact instead of the primary
reporter_contact_idintegerWho reported the issue (e.g. HOA manager)
onsite_contact_idintegerWho the technician will meet on-site
project_lead_user_idintegerInternal project lead
sales_rep_user_idintegerAccount owner / sales rep

Create a work order

curl "https://app.staffifyai.com/api/workspace/v1/workorders" \
  -X POST \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "crm_contact_id": 1893,
    "assigned_team_member_id": 42,
    "scheduled_at": "2026-10-15T09:00:00+02:00",
    "duration_minutes": 60,
    "workorder_type": "storing",
    "priority": "urgent",
    "service_address": "Lange Vijverberg 12",
    "service_postcode": "2513AC",
    "service_city": "Den Haag",
    "description": "CV werkt niet — geen warm water"
  }'

Change work order status

Status transitions follow a state-machine. Allowed transitions from each state:

open → toegewezen | geannuleerd
toegewezen → onderweg | open | geannuleerd
onderweg → uitgevoerd | geannuleerd
uitgevoerd → gefactureerd | geannuleerd
gefactureerd → (terminal)
geannuleerd → (terminal)
curl "https://app.staffifyai.com/api/workspace/v1/workorders/42/status" \
  -X POST \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "onderweg"}'

Filter the list

Query parameters: status, type, priority, assigned_team_member_id, customer_id, page, limit.

curl "https://app.staffifyai.com/api/workspace/v1/workorders?type=storing&priority=urgent" \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"

Soft-delete and restore

Deleting a work order sends it to a 30-day recycle bin. Restore within that window. Requires the operations.workorders.delete scope.

# Delete
curl -X DELETE "https://app.staffifyai.com/api/workspace/v1/workorders/42" \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"

# Restore
curl -X POST "https://app.staffifyai.com/api/workspace/v1/workorders/42/restore" \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"

Time logs (nested)

Each work order has an append-style time-log sub-resource used for payroll export (Nmbrs, Loket.nl, Visma, AFAS) and realtime "who is working now" dashboards. Time logs reuse the operations.workorders.read and operations.workorders.write scopes — no separate scope is needed.

GET/api/workspace/v1/workorders/{id}/time-logs
GET/api/workspace/v1/workorders/{id}/time-logs/{logId}
POST/api/workspace/v1/workorders/{id}/time-logs
PATCH/api/workspace/v1/workorders/{id}/time-logs/{logId}
POST/api/workspace/v1/workorders/{id}/time-logs/{logId}/stop
DELETE/api/workspace/v1/workorders/{id}/time-logs/{logId}

Fields

FieldTypeDescription
team_member_idintegerTechnician the log belongs to (immutable after create)
activity_typeenumonsite | travel | admin | break (default onsite)
started_atstring(ISO)When the activity started (defaults to NOW() on create)
ended_atstring(ISO)When the activity ended — null = still open
duration_minutesintegerServer-computed (ended_at − started_at)
notesstringFree-form technician notes

Open a timer

Omit started_at and ended_at to start a running log immediately.

curl "https://app.staffifyai.com/api/workspace/v1/workorders/42/time-logs" \
  -X POST \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "team_member_id": 7, "activity_type": "onsite" }'

Import a closed log (payroll)

Pass both started_at and ended_at to seed historical rows (e.g. during a Syntess migration). duration_minutes is computed server-side.

curl "https://app.staffifyai.com/api/workspace/v1/workorders/42/time-logs" \
  -X POST \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "team_member_id": 7,
    "activity_type": "onsite",
    "started_at": "2026-10-05T08:30:00+02:00",
    "ended_at": "2026-10-05T11:45:00+02:00",
    "notes": "Vaillant servicebeurt"
  }'

Stop a running log

Idempotent — stopping an already-closed log returns the existing row.

curl -X POST \
  "https://app.staffifyai.com/api/workspace/v1/workorders/42/time-logs/88/stop" \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "notes": "File in de spits" }'

Webhook events

The following events are emitted. Subscribe via the Webhooks endpoint.

workorder.created
workorder.updated
workorder.status_changed
workorder.deleted
workorder.restored
timelog.created
timelog.stopped
timelog.updated
timelog.deleted
Work orders - Workspace API - Staffify