DevelopersAPI reference

Visits API

REST endpoints for visits in ServFlo: 10 operations with parameters, request and response fields, permissions and curl examples.

Base URL: https://app.servflo.ai/api. Every call needs Authorization: Bearer sfk_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.

OperationEndpointRisk
List a job's visitsGET /organizations/{organizationId}/jobs/{jobId}/visitsread
Add a visit to a jobPOST /organizations/{organizationId}/jobs/{jobId}/visitswrite
Reschedule, re-note or cancel a visitPATCH /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}write
Delete a visitDELETE /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}sensitive
Assign a visit's crewPATCH /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/crewwrite
Move a visit on the dispatch boardPOST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/movewrite
On my way to a visitPOST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/on-my-waywrite
Arrived at a visitPOST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/arrivewrite
Start a visitPOST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/startwrite
Complete a visitPOST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/completewrite

List a job's visits

GET /organizations/{organizationId}/jobs/{jobId}/visits

The job's visits in order (date, then time), each with its status, field times, route position and crew (lead first). GET /jobs/{jobId} carries the same list as visits. A technician gets 404 for a job they are not on the crew of. Read-only.

Permission: jobs.read (Owner, Admin, Dispatcher, Technician (own jobs), Member); risk read (OAuth scope servflo.read).

Permissionjobs.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolvisits_list (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields

An array of objects:

FieldTypeDescription
idstring
jobIdstring
scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
scheduledTimestring or null
durationMinutesinteger
statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
notesstring or null
sortOrderinteger
routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
onMyWayAtstring (date-time) or null
arrivedAtstring (date-time) or null
startedAtstring (date-time) or null
completedAtstring (date-time) or null
createdAtstring (date-time)
updatedAtstring (date-time)
crewarray of objectTechnicians on the visit, lead first.
crew[].technicianIdstring
crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
crew[].namestring
crew[].colorstring

Add a visit to a job

POST /organizations/{organizationId}/jobs/{jobId}/visits

Adds a visit (e.g. day 2 of an install) with an optional date, time, duration and crew. Without scheduledDate it waits on the dispatch board's 'Needs scheduling' list. The job then mirrors its next open visit (its date, time and technician may change). Nothing is sent to anyone. Get technician ids from GET /technicians/options.

Permissionjobs.write (Owner, Admin, Dispatcher)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_create (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes

Request body

FieldTypeRequiredDescription
scheduledDatestring or nullNoYYYY-MM-DD; omit to leave it unscheduled.
scheduledTimestring or nullNoA local time of day, HH:MM (24-hour). Blank means 09:00 on new jobs.
durationMinutesintegerNoMinutes; defaults to the job's duration. 5–1440.
notesstring or nullNoWhat this visit is for (shown to the crew).
customFieldsobjectNoCustom field values by key (definitions: GET /custom-fields?entity=VISIT); merged into the saved values on an update, null clears one. Checked against each field's type and options; unknown keys are refused.
technicianIdsarray of stringNoTechnicians on the visit's crew (GET /technicians/options). Empty = unassigned. Up to 20 items.
leadTechnicianIdstring or nullNoWho leads the visit; defaults to the first technician listed.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scheduledDate":"2026-11-03","scheduledTime":"09:00","durationMinutes":60}'

Response

201: the answer's fields
FieldTypeDescription
idstring
jobIdstring
scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
scheduledTimestring or null
durationMinutesinteger
statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
notesstring or null
sortOrderinteger
routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
onMyWayAtstring (date-time) or null
arrivedAtstring (date-time) or null
startedAtstring (date-time) or null
completedAtstring (date-time) or null
createdAtstring (date-time)
updatedAtstring (date-time)
crewarray of objectTechnicians on the visit, lead first.
crew[].technicianIdstring
crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
crew[].namestring
crew[].colorstring

Reschedule, re-note or cancel a visit

PATCH /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}

Changes a visit's date (null takes it off the calendar), time, duration or notes, or sets its status to SCHEDULED, CONFIRMED or CANCELLED (reopening a completed visit clears its completion). Start and complete with POST …/start and …/complete instead. The job re-mirrors its next open visit: cancelling the last open visit completes or cancels the job. Nothing is sent to the customer.

Permissionjobs.write (Owner, Admin, Dispatcher)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_update (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Request body

FieldTypeRequiredDescription
scheduledDatestring or nullNoYYYY-MM-DD, or null to unschedule.
scheduledTimestring or nullNoA local time of day, HH:MM (24-hour). Blank means 09:00 on new jobs.
durationMinutesintegerNoMinutes on site. 5–1440.
notesstring or nullNo
customFieldsobjectNoCustom field values by key (definitions: GET /custom-fields?entity=VISIT); merged into the saved values on an update, null clears one. Checked against each field's type and options; unknown keys are refused.
statusenumNoOne of SCHEDULED, CONFIRMED, CANCELLED.

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scheduledDate":"2026-11-03","scheduledTime":"09:00","durationMinutes":60}'

Response

200: the answer's fields
FieldTypeDescription
idstring
jobIdstring
scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
scheduledTimestring or null
durationMinutesinteger
statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
notesstring or null
sortOrderinteger
routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
onMyWayAtstring (date-time) or null
arrivedAtstring (date-time) or null
startedAtstring (date-time) or null
completedAtstring (date-time) or null
createdAtstring (date-time)
updatedAtstring (date-time)
crewarray of objectTechnicians on the visit, lead first.
crew[].technicianIdstring
crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
crew[].namestring
crew[].colorstring

Delete a visit

DELETE /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}

Destructive: removes the visit and its crew; time logged on it stays on the job. A job keeps at least one visit (409 for its only one: cancel it or delete the job). The job re-mirrors its next open visit.

Permissionjobs.delete (Owner, Admin, Dispatcher)
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolvisits_delete (toolset scheduling)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Example

curl -X DELETE "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
oktrue

Assign a visit's crew

PATCH /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/crew

Replaces the visit's crew: any number of technicians, one of them the lead (the job's technician mirrors its next open visit's lead). An empty list unassigns the visit. Technicians on any visit's crew see the job, its customer and property. Nothing is sent to anyone.

Permissionjobs.assign (Owner, Admin, Dispatcher)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_setCrew (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Request body

FieldTypeRequiredDescription
technicianIdsarray of stringYesTechnicians on the visit's crew (GET /technicians/options). Empty = unassigned. Up to 20 items.
leadTechnicianIdstring or nullNoWho leads the visit; defaults to the first technician listed.

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}/crew" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"technicianIds":["technician_id"]}'

Response

200: the answer's fields
FieldTypeDescription
idstring
jobIdstring
scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
scheduledTimestring or null
durationMinutesinteger
statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
notesstring or null
sortOrderinteger
routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
onMyWayAtstring (date-time) or null
arrivedAtstring (date-time) or null
startedAtstring (date-time) or null
completedAtstring (date-time) or null
createdAtstring (date-time)
updatedAtstring (date-time)
crewarray of objectTechnicians on the visit, lead first.
crew[].technicianIdstring
crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
crew[].namestring
crew[].colorstring

Move a visit on the dispatch board

POST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/move

The board's drag and drop: sets the visit's date, start time and (optionally, after a resize) duration, and moves it from one technician to another (fromTechnicianId leaves the crew, toTechnicianId joins, as lead if the one leaving led; toTechnicianId: null unassigns). Returns the visit and conflicts for the technician it landed on (overlapping visits, outside working hours): warnings only, the move is saved. Only open visits move (409 otherwise).

Permissionjobs.assign (Owner, Admin, Dispatcher)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_move (toolset scheduling, in the default set)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Request body

FieldTypeRequiredDescription
scheduledDatestringYesA calendar date in the business's own calendar, YYYY-MM-DD.
scheduledTimestringYesA local time of day, HH:MM (24-hour). Blank means 09:00 on new jobs.
durationMinutesintegerNoMinutes on site. 5–1440.
fromTechnicianIdstring or nullNoThe technician row it was dragged from (null: the Unassigned row).
toTechnicianIdstring or nullNoThe technician row it was dropped on (null: unassign). Omit to keep the crew.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}/move" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scheduledDate":"2026-11-03","scheduledTime":"09:00"}'

Response

200: the answer's fields
FieldTypeDescription
visitobject
visit.idstring
visit.jobIdstring
visit.scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
visit.scheduledTimestring or null
visit.durationMinutesinteger
visit.statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
visit.notesstring or null
visit.sortOrderinteger
visit.routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
visit.onMyWayAtstring (date-time) or null
visit.arrivedAtstring (date-time) or null
visit.startedAtstring (date-time) or null
visit.completedAtstring (date-time) or null
visit.createdAtstring (date-time)
visit.updatedAtstring (date-time)
visit.crewarray of objectTechnicians on the visit, lead first.
visit.crew[].technicianIdstring
visit.crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
visit.crew[].namestring
visit.crew[].colorstring
conflictsarray of object
conflicts[].kindenumOverlap: another open visit of the technician at the same time; OUTSIDE_HOURS: outside their working hours. One of OVERLAP, OUTSIDE_HOURS.
conflicts[].withIdstringFor OVERLAP: the visit it overlaps.

On my way to a visit

POST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/on-my-way

Marks a SCHEDULED or CONFIRMED visit 'On the way' (onMyWayAt, shown live on the dispatch board) and starts the caller's TRAVEL time (a linked technician; else the visit's lead). Emits technician.on_my_way; no text is sent to the customer yet. Technicians only for visits they are on the crew of (404 otherwise).

Permissionjobs.work (Owner, Admin, Dispatcher, Technician)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_onMyWay (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}/on-my-way" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
jobobject
job.idstring
job.identifierstring
job.statusenumThe job's rolled-up status (its next open visit's). One of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
job.onMyWayAtstring (date-time) or null
job.arrivedAtstring (date-time) or null
job.startedAtstring (date-time) or null
job.completedAtstring (date-time) or null
job.completionNotesstring or null
job.nextJobIdstring or nullThe next occurrence booked when this completed a recurring job.
visitobject
visit.idstring
visit.jobIdstring
visit.scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
visit.scheduledTimestring or null
visit.durationMinutesinteger
visit.statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
visit.notesstring or null
visit.sortOrderinteger
visit.routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
visit.onMyWayAtstring (date-time) or null
visit.arrivedAtstring (date-time) or null
visit.startedAtstring (date-time) or null
visit.completedAtstring (date-time) or null
visit.createdAtstring (date-time)
visit.updatedAtstring (date-time)
visit.crewarray of objectTechnicians on the visit, lead first.
visit.crew[].technicianIdstring
visit.crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
visit.crew[].namestring
visit.crew[].colorstring

Arrived at a visit

POST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/arrive

Stamps the visit's arrivedAt (the office sees 'Arrived') and stops the caller's TRAVEL time for the job. The visit is not started; call POST …/start for that.

Permissionjobs.work (Owner, Admin, Dispatcher, Technician)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_arrive (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}/arrive" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
jobobject
job.idstring
job.identifierstring
job.statusenumThe job's rolled-up status (its next open visit's). One of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
job.onMyWayAtstring (date-time) or null
job.arrivedAtstring (date-time) or null
job.startedAtstring (date-time) or null
job.completedAtstring (date-time) or null
job.completionNotesstring or null
job.nextJobIdstring or nullThe next occurrence booked when this completed a recurring job.
visitobject
visit.idstring
visit.jobIdstring
visit.scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
visit.scheduledTimestring or null
visit.durationMinutesinteger
visit.statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
visit.notesstring or null
visit.sortOrderinteger
visit.routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
visit.onMyWayAtstring (date-time) or null
visit.arrivedAtstring (date-time) or null
visit.startedAtstring (date-time) or null
visit.completedAtstring (date-time) or null
visit.createdAtstring (date-time)
visit.updatedAtstring (date-time)
visit.crewarray of objectTechnicians on the visit, lead first.
visit.crew[].technicianIdstring
visit.crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
visit.crew[].namestring
visit.crew[].colorstring

Start a visit

POST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/start

Sets the visit IN_PROGRESS (startedAt; arriving too if not yet), stops travel and any other running job clock and starts JOB time on this visit. The job becomes IN_PROGRESS. Emits job.started. Nothing is sent to the customer.

Permissionjobs.work (Owner, Admin, Dispatcher, Technician)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_start (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}/start" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
jobobject
job.idstring
job.identifierstring
job.statusenumThe job's rolled-up status (its next open visit's). One of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
job.onMyWayAtstring (date-time) or null
job.arrivedAtstring (date-time) or null
job.startedAtstring (date-time) or null
job.completedAtstring (date-time) or null
job.completionNotesstring or null
job.nextJobIdstring or nullThe next occurrence booked when this completed a recurring job.
visitobject
visit.idstring
visit.jobIdstring
visit.scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
visit.scheduledTimestring or null
visit.durationMinutesinteger
visit.statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
visit.notesstring or null
visit.sortOrderinteger
visit.routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
visit.onMyWayAtstring (date-time) or null
visit.arrivedAtstring (date-time) or null
visit.startedAtstring (date-time) or null
visit.completedAtstring (date-time) or null
visit.createdAtstring (date-time)
visit.updatedAtstring (date-time)
visit.crewarray of objectTechnicians on the visit, lead first.
visit.crew[].technicianIdstring
visit.crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
visit.crew[].namestring
visit.crew[].colorstring

Complete a visit

POST /organizations/{organizationId}/jobs/{jobId}/visits/{visitId}/complete

Sets the visit COMPLETED and stops the job's JOB and TRAVEL time. When it was the job's last open visit the job completes too: then the job's required forms must be answered (400 naming the missing fields; fill them with PATCH /jobs/{jobId}/forms/{formId}), complete drafts are submitted, and a recurring job books its next occurrence (job.nextJobId, emits job.completed). With more visits scheduled, the job stays open on the next one.

Permissionjobs.work (Owner, Admin, Dispatcher, Technician)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolvisits_complete (toolset scheduling)

Parameters

NameInTypeRequiredDescription
jobIdpathstringYes
visitIdpathstringYes

Request body

FieldTypeRequiredDescription
completionNotesstring or nullNoWhat was done; saved on the job.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/visits/{visitId}/complete" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"completionNotes":"string"}'

Response

200: the answer's fields
FieldTypeDescription
jobobject
job.idstring
job.identifierstring
job.statusenumThe job's rolled-up status (its next open visit's). One of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
job.onMyWayAtstring (date-time) or null
job.arrivedAtstring (date-time) or null
job.startedAtstring (date-time) or null
job.completedAtstring (date-time) or null
job.completionNotesstring or null
job.nextJobIdstring or nullThe next occurrence booked when this completed a recurring job.
visitobject
visit.idstring
visit.jobIdstring
visit.scheduledDatestring or nullYYYY-MM-DD; null = not scheduled yet.
visit.scheduledTimestring or null
visit.durationMinutesinteger
visit.statusenumOne of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED.
visit.notesstring or null
visit.sortOrderinteger
visit.routeOrderinteger or nullPosition in the technician's route for the day (Optimize route); null = by time.
visit.onMyWayAtstring (date-time) or null
visit.arrivedAtstring (date-time) or null
visit.startedAtstring (date-time) or null
visit.completedAtstring (date-time) or null
visit.createdAtstring (date-time)
visit.updatedAtstring (date-time)
visit.crewarray of objectTechnicians on the visit, lead first.
visit.crew[].technicianIdstring
visit.crew[].isLeadbooleanThe lead runs the visit; the job's technician is its lead.
visit.crew[].namestring
visit.crew[].colorstring

On this page