WorkestraDocs
ModulesField Service

Service Contracts

Recurring service agreements — generate work orders on a cadence, track included visits, enforce SLAs.

A service contract is a recurring agreement with a customer to provide periodic service — quarterly HVAC maintenance, monthly inspections, annual safety audits, whatever the cadence. Workestra generates the visits automatically; you can also link individual ad-hoc work orders to the contract for billing or reporting.

When to use a service contract

Use a contract when…Don't bother when…
The same customer needs the same service repeatedly on a scheduleOne-off jobs (raise a work order directly)
You want SLA terms (response + resolution times) auto-applied to every related WOYou quote and invoice each job individually
You want a "next visit due" date that auto-rollsYou want full pricing flexibility per job

A contract's monthly fee and/or included-visit count can also drive billing automation downstream (full Finance bridge queued for Phase 4.8).

Creating a service contract

Go to /fsm/service-contractsNew service contract.

FieldNotes
NumberAuto-allocated SC-YYYY-NNNN
Name + DescriptionInternal labels
CustomerContact and/or company from CRM
Statusdraft / active / paused / expired / cancelled
Starts on / Ends onContract dates
Auto-renewBoolean — when the end date passes, auto-extend
Cadenceweekly / biweekly / monthly / quarterly / semiannual / annual / custom
Cadence interval + unitFor custom cadence: every N days/weeks/months
SLA response minutesDefault response SLA for related work orders
SLA resolution minutesDefault resolution SLA for related work orders
Included visits per cycleE.g. "2 visits per quarter"
Hourly rate / Flat fee per visit / Monthly feePricing knobs (used by reporting today; bridge to Finance queued)
Currency3-letter ISO
TermsFree text — the actual contract language
Asset IDsArray of assets under this contract

Visit generation

Every day at 13:30 UTC, a Vercel cron job (fsm-service-contract-due-renewals) scans active contracts and:

  1. Looks at the contract's cadence and last_visit_generated_at.
  2. Computes the next due date.
  3. Inserts a row into fsm_service_contract_visits if one doesn't already exist for that contract + date.
  4. Updates next_visit_due_on on the contract.

A fsm_service_contract_visit row sits in status planned until a dispatcher converts it to a work order — at which point the visit links to the new WO via work_order_id.

Auto-materializing each visit into a fully-formed work order from a template (with line items + parts + required skills pre-filled) is queued for Phase 4.10 alongside RRULE-based cadences. Today the cron generates the visit row; the dispatcher promotes it to a WO with a one-click action.

Linking ad-hoc work orders

A work order that wasn't auto-generated can still link to a contract — set the WO's service_contract_id. This:

  • Inherits the contract's SLA defaults onto the WO
  • Counts toward "included visits per cycle" tracking
  • Shows up under the contract's history view

Cadence options

CadenceGenerated every…
weekly7 days
biweekly14 days
monthlySame day of the month, next month
quarterlySame day, +3 months
semiannualSame day, +6 months
annualSame day, +1 year
customcadence_interval × cadence_unit (e.g. every 45 days)

Limitations of the v1 cadence engine:

  • "First Tuesday of every quarter" — not supported
  • "Twice a year, in March and September" — not supported (use custom every 6 months starting March)
  • Skipping holidays — not yet

Richer scheduling (RRULE-based) is queued for Phase 4.10. Today's enum + interval covers ~90% of common recurring service cases.

Pausing a contract

Use status = paused. The cron skips paused contracts entirely — no new visit rows generated. Past visits remain linked. Resume by flipping back to active.

Expiring vs cancelling

StatusWhenCron behavior
expiredPast ends_on without auto-renewSkipped
cancelledExplicit terminationSkipped

Both are terminal in practice — to revive, flip back to active and set a fresh ends_on.

What's on a generated visit

FieldNotes
Visit due onDate the visit should happen
Statusplanned / scheduled (WO created) / completed / skipped / cancelled
Work orderSet when the visit is promoted to a WO
NotesFree text

Unique constraint on (workspace_id, service_contract_id, visit_due_on) — re-running the cron never duplicates a date.