Skip to main content
Staffify Docs
Updated
Docs/Workspace API/Maintenance contracts

Maintenance contracts

Recurring maintenance commitments (yearly CV-ketel check, quarterly VvE round, custom intervals) between your business and a customer. Each contract can have one or more installations (CV-ketel, warmtepomp, airco, lift, etc). An auto-generate cron creates work orders ahead of each scheduled visit based on the contract's frequency.
Scopes: operations.maintenance.read / operations.maintenance.write / operations.maintenance.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/maintenance-contracts
GET/api/workspace/v1/maintenance-contracts/{id}
POST/api/workspace/v1/maintenance-contracts
PATCH/api/workspace/v1/maintenance-contracts/{id}
DELETE/api/workspace/v1/maintenance-contracts/{id}
POST/api/workspace/v1/maintenance-contracts/{id}/restore
GET/api/workspace/v1/maintenance-contracts/{id}/installations
POST/api/workspace/v1/maintenance-contracts/{id}/installations
DELETE/api/workspace/v1/maintenance-contracts/{id}/installations/{installationId}

The maintenance contract object

FieldTypeDescription
contract_numberstringAuto-generated, e.g. MC-2026-00001
contract_namestringHuman-readable label (required)
hub_customer_idintegerAdvanced Booking customer id — one of hub_customer_id or crm_contact_id is required on create
crm_contact_idintegerCRM contact id — alternative to hub_customer_id (auto-bridges to a hub customer)
frequencyenumweekly | monthly | quarterly | semi_annually | yearly | custom_days
custom_interval_daysintegerRequired when frequency = custom_days (e.g. 90 = every 90 days)
service_descriptionstringWhat the service covers (e.g. CV-ketel onderhoudsbeurt)
price_per_visitnumberFixed price in EUR per auto-generated visit
duration_minutesintegerExpected visit duration (default 60)
assigned_team_member_idintegerDefault technician for auto-generated work orders
starts_atstring(ISO)Start date of the contract (required)
ends_atstring(ISO)Optional end date — null = runs indefinitely
next_generate_atstring(ISO)When the auto-generate cron will next create a work order
last_generated_atstring(ISO)When the last auto-generated work order was created
last_generated_workorder_idintegerid of the most recent auto-generated work order
activebooleanPaused if false — auto-generate stops
created_atstring(ISO)When the contract was created
deleted_atstring(ISO)Soft-delete timestamp — null if active

Create a contract

curl "https://app.staffifyai.com/api/workspace/v1/maintenance-contracts" \
  -X POST \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contract_name": "Jaarlijks onderhoud VvE Lange Vijverberg",
    "crm_contact_id": 1893,
    "frequency": "yearly",
    "service_description": "APK CV-ketel + legionellacontrole",
    "price_per_visit": 165.00,
    "duration_minutes": 90,
    "starts_at": "2026-11-01T09:00:00+01:00"
  }'

Installations (nested)

Each contract can list the physical units it covers. Installations use the same operations.maintenance scopes as the parent contract — there is no separate scope.

FieldTypeDescription
contract_idintegerParent contract id
installation_namestringShort label (e.g. "CV Vaillant TurboTec Pro")
installation_typestringCategory (e.g. "CV-ketel", "warmtepomp", "airco")
serial_numberstringManufacturer serial for warranty/compliance
installation_datestring(ISO)When the unit was installed on-site
notesstringTechnician notes (max 2000 chars)
curl "https://app.staffifyai.com/api/workspace/v1/maintenance-contracts/42/installations" \
  -X POST \
  -H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "installation_name": "CV Vaillant TurboTec Pro",
    "installation_type": "CV-ketel",
    "serial_number": "VLT-2021-884433"
  }'

Soft-delete and restore

DELETE moves the contract to the 30-day recycle bin by setting deleted_at. Default list queries exclude deleted rows; append ?deleted=true to see only deleted or ?deleted=any to see both. POST to /{id}/restore lifts the deletion. After 30 days the purge cron hard-deletes the row and all installations under it.

Events

  • maintenance_contract.created
  • maintenance_contract.updated
  • maintenance_contract.deleted
  • maintenance_contract.restored
  • maintenance_installation.added
  • maintenance_installation.removed
Maintenance contracts - Workspace API - Staffify