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.
403 MODULE_VAKMAN_DISABLED otherwise, even when the API key carries the right scopes.Endpoints
/api/workspace/v1/workorders/api/workspace/v1/workorders/{id}/api/workspace/v1/workorders/api/workspace/v1/workorders/{id}/api/workspace/v1/workorders/{id}/status/api/workspace/v1/workorders/{id}/api/workspace/v1/workorders/{id}/restoreThe work order object
| Field | Type | Description |
|---|---|---|
| workorder_number | string | Auto-generated, e.g. WO-2026-00001 |
| status | enum | open | toegewezen | onderweg | uitgevoerd | gefactureerd | geannuleerd |
| workorder_type | enum | service | storing | onderhoud | montage | offerte | garantie (default service) |
| priority | enum | low | normal | high | urgent (default normal) |
| crm_contact_id | integer | Primary customer contact |
| assigned_team_member_id | integer | Technician assigned to the job |
| scheduled_at | string(ISO) | When the visit is planned |
| duration_minutes | integer | Expected job duration in minutes (default 60) |
| time_window_minutes | integer | Customer-facing arrival window (e.g. 120 = "between 10-12") |
| service_address | string | Work address |
| service_postcode | string | Work address postcode |
| service_city | string | Work address city |
| description | string | Customer-reported issue |
| internal_notes | string | Technician notes, not shown to customer |
| customer_notes | string | Notes shown to customer on completion |
| reported_at | string(ISO) | When the issue was reported (SLA reaction time tracking) |
| installation_id | integer | Links to a specific installation under a maintenance contract |
| object_name | string | Building/complex name (e.g. "HOA Lange Vijverberg 12") |
| buyer_reference | string | Customer PO number — forwarded to invoice BuyerReference (Peppol) |
| billing_contact_id | integer | Billing contact override — invoice goes to this contact instead of the primary |
| reporter_contact_id | integer | Who reported the issue (e.g. HOA manager) |
| onsite_contact_id | integer | Who the technician will meet on-site |
| project_lead_user_id | integer | Internal project lead |
| sales_rep_user_id | integer | Account 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:
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.
/api/workspace/v1/workorders/{id}/time-logs/api/workspace/v1/workorders/{id}/time-logs/{logId}/api/workspace/v1/workorders/{id}/time-logs/api/workspace/v1/workorders/{id}/time-logs/{logId}/api/workspace/v1/workorders/{id}/time-logs/{logId}/stop/api/workspace/v1/workorders/{id}/time-logs/{logId}Fields
| Field | Type | Description |
|---|---|---|
| team_member_id | integer | Technician the log belongs to (immutable after create) |
| activity_type | enum | onsite | travel | admin | break (default onsite) |
| started_at | string(ISO) | When the activity started (defaults to NOW() on create) |
| ended_at | string(ISO) | When the activity ended — null = still open |
| duration_minutes | integer | Server-computed (ended_at − started_at) |
| notes | string | Free-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.