ServiceKeel.Developers
All 9 chapters · Code and API details

CHAPTER 04 OF 8

Reschedule, track, and cancel work

Use existing record IDs and current versions to answer follow-up calls without duplicating the original job.

1. Find the right job and visit

After verifying the caller, list their jobs and the selected job’s visits. Confirm which appointment they mean, then load the current record details. Keep the latest version from every response; another user may have changed the schedule since the original call.

GET/field_service/jobs

List jobs

Parameters from the API specification
NameLocationType / schema
searchquerystring
statusquerystring
customer_idquerystring
property_idquerystring
job_idquerystring
limitqueryinteger
offsetqueryinteger
created_afterquerystring
created_beforequerystring

Documented responses: 200 401 403

Open full API reference
GET/field_service/jobs/{id}

Get job

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring

Documented responses: 200 404

Open full API reference
GET/field_service/visits

List visits

Parameters from the API specification
NameLocationType / schema
searchquerystring
statusquerystring
customer_idquerystring
property_idquerystring
job_idquerystring
limitqueryinteger
offsetqueryinteger
created_afterquerystring
created_beforequerystring

Documented responses: 200 401 403

Open full API reference
GET/field_service/visits/{id}

Get visit

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring

Documented responses: 200 404

Open full API reference
Reload the records before editing
const jobs = await api('GET', '/jobs?customer_id=' + customer.id);
// callerSelectedJobId is selected from jobs, not an unverified external ID.
job = await api('GET', '/jobs/' + callerSelectedJobId);
const visits = await api('GET', '/visits?job_id=' + job.id);
visit = await api('GET', '/visits/' + callerSelectedVisitId);
JavaScript · server side

2. Move the appointment

“Could we move that appointment?” does not specify a replacement time. Repeat the booking chapter’s conversation: ask for a new date and window, fetch matching availability, offer times, wait for a choice, read back the exact replacement, and wait for an explicit yes. Until then, leave the existing appointment unchanged. Use the server-held confirmed proposal as replacementSlot; do not select the first later slot automatically.

After that confirmation, patch the existing visit with the new slot and current version, then save relevant job context. The API releases the old time when the reschedule succeeds. A conflict requires a fresh choice and confirmation.

PATCH/field_service/visits/{id}

Update visit (PATCH)

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring
recordrequiredbodymodels.FieldServiceRecord

Documented responses: 200 400 409

Open full API reference
PATCH/field_service/jobs/{id}

Update job (PATCH)

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring
recordrequiredbodymodels.FieldServiceRecord

Documented responses: 200 400 409

Open full API reference
Save the caller-confirmed replacement slot
visit = await api('PATCH', '/visits/' + visit.id, {
  version: visit.version,
  starts_at: replacementSlot.starts_at, ends_at: replacementSlot.ends_at,
  timezone: replacementSlot.timezone,
  assigned_user_ids: [replacementSlot.eligible_staff_ids[0]],
  notify_customer: false, notify_assigned_users: false,
});
job = await api('PATCH', '/jobs/' + job.id, {
  version: job.version,
  notes: 'Caller requested a later quote appointment.',
});
JavaScript · server side
The mock receptionist confirms that the appointment moved and the original slot was released.
FROM THE WORKING EXAMPLE

The integration verifies no appointment changes before the caller’s final yes, persistence of the second offered time the caller selected, and availability of the released slot.

Select the image to view it at full size.

3. Report technician status

When the caller asks “Where is my technician?”, read the visit’s tracking response. Report only the status and ETA the business actually provides. An absent ETA should lead to a handoff or an honest “I don’t have an arrival estimate yet.”

The demo simulates an authorized technician progressing through en_route, arrive, start, and complete. In a live integration, those writes need an authorized dispatch or technician event; a status question from a caller is not permission to advance the work.

GET/field_service/visits/{id}/tracking

Read an appointment's dispatch status, arrival window and available ETA

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring

Documented responses: 200

Open full API reference
POST/field_service/visits/{id}/actions

Run visit action

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring
actionrequiredbodygin.FieldServiceActionRequest

Documented responses: 200 400 403 409

Open full API reference
POST/field_service/jobs/{id}/actions

Run job action

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring
actionrequiredbodygin.FieldServiceActionRequest

Documented responses: 200 400 403 409

Open full API reference
Read status; demonstrate a separate authorized lifecycle update
const tracking = await api('GET', '/visits/' + visit.id + '/tracking');
// Report tracking to the caller. The following is demo dispatch simulation:
for (const action of ['en_route', 'arrive', 'start', 'complete']) {
  visit = await api('POST', '/visits/' + visit.id + '/actions', {
    action, version: visit.version,
  });
}
job = await api('POST', '/jobs/' + job.id + '/actions', {
  action: 'complete', version: job.version,
});
JavaScript · server side

4. Cancel with an explicit scope

Confirm the job, reason, and which appointments should be cancelled. Supported scopes are job_only, future_appointments, and all_open_appointments. Completed visits are retained.

The demo first runs a separate “Book a follow-up” conversation, collecting the caller’s date, window, selected slot, and explicit confirmation before creating its job and visit. A later “Cancel a follow-up” step cancels those existing records. A cancellation request never creates a new appointment.

Cancellation processes at most 25 visits per call. If complete is false, follow the reference’s continuation contract using the same reason and scope. Do not announce that everything is cancelled until the operation is complete.

POST/field_service/jobs

Create job

Parameters from the API specification
NameLocationType / schema
recordrequiredbodymodels.FieldServiceRecord

Documented responses: 201 400 409

Open full API reference
POST/field_service/jobs/{id}/cancel

Cancel a job and optionally its open appointments in resumable batches

Parameters from the API specification
NameLocationType / schema
idrequiredpathstring
cancellationrequiredbodygin.fieldServiceCancelJobRequest

Documented responses: 200 409

Open full API reference
Cancel only the confirmed optional follow-up
// Earlier call: ONLY after the caller confirms the follow-up's exact slot.
const followup = await api('POST', '/jobs', {
  title: 'Optional follow-up', customer_id: customer.id, property_id: location.id,
});
// Save its visit with that confirmed slot using the previous chapter's flow.
// Persist both IDs and return the success to the caller.

// Later cancellation request: reload the existing follow-up and confirm scope.
// Do not create any new job or visit here.
const currentFollowup = await api('GET', '/jobs/' + followup.id);
const cancellation = await api('POST', '/jobs/' + followup.id + '/cancel', {
  version: currentFollowup.version,
  reason: 'Caller no longer needs the optional follow-up.',
  scope: 'all_open_appointments',
});
// Inspect cancellation.complete and the returned job before confirming.
JavaScript · server side
The receptionist confirms cancellation of the extra follow-up while preserving the completed repair.
FROM THE WORKING EXAMPLE

The job and its open appointment are cancelled together, with a reason attached.

Select the image to view it at full size.