openapi: 3.1.0
info:
title: ServiceTitan Job Planning & Management API
description: |
The JPM API manages jobs, projects, appointments, job types, call reasons, job cancel
reasons, and job history. It is the operational core of ServiceTitan — every work order
and recurring service rolls through this surface. Tenant-scoped; OAuth 2.0 + App Key.
version: "2.0.0"
contact:
name: ServiceTitan Developer Support
url: https://developer.servicetitan.io/
email: [email protected]
license:
name: ServiceTitan Terms of Service
url: https://www.servicetitan.com/legal/terms-of-service
servers:
- url: https://api.servicetitan.io/jpm/v2/{tenant}
description: Production
variables:
tenant:
default: "0000000"
- url: https://api-integration.servicetitan.io/jpm/v2/{tenant}
description: Integration (Sandbox)
variables:
tenant:
default: "0000000"
security:
- OAuth2: []
AppKey: []
tags:
- name: Jobs
description: Service jobs (work orders)
- name: Appointments
description: Job appointments and visits
- name: Projects
description: Multi-job projects
- name: Job Types
description: Job type definitions
- name: Call Reasons
description: Job booking call reasons
paths:
/jobs:
get:
summary: List Jobs
operationId: listJobs
tags: [Jobs]
parameters:
- $ref: '#/components/parameters/Page'
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/ModifiedOnOrAfter'
- $ref: '#/components/parameters/JobStatus'
responses:
'200':
description: Paginated list of jobs
content:
application/json:
schema:
$ref: '#/components/schemas/JobPagedResponse'
post:
summary: Create Job
operationId: createJob
tags: [Jobs]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/JobCreateRequest'
responses:
'200':
description: Created job
content:
application/json:
schema:
$ref: '#/components/schemas/Job'
/jobs/{id}:
get:
summary: Get Job
operationId: getJob
tags: [Jobs]
parameters:
- $ref: '#/components/parameters/Id'
responses:
'200':
description: Job record
content:
application/json:
schema:
$ref: '#/components/schemas/Job'
patch:
summary: Update Job
operationId: updateJob
tags: [Jobs]
parameters:
- $ref: '#/components/parameters/Id'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/JobUpdateRequest'
responses:
'200':
description: Updated job
content:
application/json:
schema:
$ref: '#/components/schemas/Job'
/jobs/{id}/cancel:
put:
summary: Cancel Job
operationId: cancelJob
tags: [Jobs]
parameters:
- $ref: '#/components/parameters/Id'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
reasonId: { type: integer }
memo: { type: string }
responses:
'200':
description: Job cancelled
/jobs/{id}/hold:
put:
summary: Hold Job
operationId: holdJob
tags: [Jobs]
parameters:
- $ref: '#/components/parameters/Id'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
reasonId: { type: integer }
memo: { type: string }
responses:
'200':
description: Job placed on hold
/jobs/{id}/history:
get:
summary: Get Job History
operationId: getJobHistory
tags: [Jobs]
parameters:
- $ref: '#/components/parameters/Id'
responses:
'200':
description: Job history
content:
application/json:
schema:
$ref: '#/components/schemas/JobHistory'
/appointments:
get:
summary: List Appointments
operationId: listAppointments
tags: [Appointments]
parameters:
- $ref: '#/components/parameters/Page'
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/ModifiedOnOrAfter'
- name: jobId
in: query
schema: { type: integer }
responses:
'200':
description: Paginated list of appointments
content:
application/json:
schema:
$ref: '#/components/schemas/AppointmentPagedResponse'
post:
summary: Create Appointment
operationId: createAppointment
tags: [Appointments]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AppointmentCreateRequest'
responses:
'200':
description: Created appointment
content:
application/json:
schema:
$ref: '#/components/schemas/Appointment'
/appointments/{id}:
get:
summary: Get Appointment
operationId: getAppointment
tags: [Appointments]
parameters:
- $ref: '#/components/parameters/Id'
responses:
'200':
description: Appointment record
content:
application/json:
schema:
$ref: '#/components/schemas/Appointment'
patch:
summary: Update Appointment
operationId: updateAppointment
tags: [Appointments]
parameters:
- $ref: '#/components/parameters/Id'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AppointmentUpdateRequest'
responses:
'200':
description: Updated appointment
content:
application/json:
schema:
$ref: '#/components/schemas/Appointment'
/appointments/{id}/reschedule:
patch:
summary: Reschedule Appointment
operationId: rescheduleAppointment
tags: [Appointments]
parameters:
- $ref: '#/components/parameters/Id'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [start, end]
properties:
start: { type: string, format: date-time }
end: { type: string, format: date-time }
arrivalWindowStart: { type: string, format: date-time }
arrivalWindowEnd: { type: string, format: date-time }
responses:
'200':
description: Appointment rescheduled
/projects:
get:
summary: List Projects
operationId: listProjects
tags: [Projects]
parameters:
- $ref: '#/components/parameters/Page'
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/ModifiedOnOrAfter'
responses:
'200':
description: Paginated list of projects
content:
application/json:
schema:
$ref: '#/components/schemas/ProjectPagedResponse'
post:
summary: Create Project
operationId: createProject
tags: [Projects]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProjectCreateRequest'
responses:
'200':
description: Created project
content:
application/json:
schema:
$ref: '#/components/schemas/Project'
/projects/{id}:
get:
summary: Get Project
operationId: getProject
tags: [Projects]
parameters:
- $ref: '#/components/parameters/Id'
responses:
'200':
description: Project record
content:
application/json:
schema:
$ref: '#/components/schemas/Project'
/job-types:
get:
summary: List Job Types
operationId: listJobTypes
tags: [Job Types]
responses:
'200':
description: Job types
content:
application/json:
schema:
$ref: '#/components/schemas/JobTypePagedResponse'
/job-cancel-reasons:
get:
summary: List Job Cancel Reasons
operationId: listJobCancelReasons
tags: [Jobs]
responses:
'200':
description: Cancel reasons
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
type: object
properties:
id: { type: integer }
name: { type: string }
active: { type: boolean }
components:
securitySchemes:
OAuth2:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://auth.servicetitan.io/connect/token
scopes: {}
AppKey:
type: apiKey
in: header
name: ST-App-Key
parameters:
Id:
name: id
in: path
required: true
schema: { type: integer, format: int64 }
Page:
name: page
in: query
schema: { type: integer, default: 1 }
PageSize:
name: pageSize
in: query
schema: { type: integer, default: 50, maximum: 500 }
ModifiedOnOrAfter:
name: modifiedOnOrAfter
in: query
schema: { type: string, format: date-time }
JobStatus:
name: jobStatus
in: query
schema: { type: string, enum: [Scheduled, Dispatched, InProgress, Hold, Completed, Canceled] }
schemas:
Job:
type: object
properties:
id: { type: integer, format: int64 }
jobNumber: { type: string }
customerId: { type: integer, format: int64 }
locationId: { type: integer, format: int64 }
jobStatus: { type: string }
completedOn: { type: string, format: date-time, nullable: true }
businessUnitId: { type: integer }
jobTypeId: { type: integer }
priority: { type: string }
campaignId: { type: integer, nullable: true }
summary: { type: string }
customFields:
type: array
items:
type: object
properties:
typeId: { type: integer }
name: { type: string }
value: { type: string }
appointmentCount: { type: integer }
firstAppointmentId: { type: integer, nullable: true }
lastAppointmentId: { type: integer, nullable: true }
recallForId: { type: integer, nullable: true }
warrantyId: { type: integer, nullable: true }
jobGeneratedLeadSource:
type: object
properties:
jobId: { type: integer, nullable: true }
employeeId: { type: integer, nullable: true }
noCharge: { type: boolean }
notificationsEnabled: { type: boolean }
createdOn: { type: string, format: date-time }
modifiedOn: { type: string, format: date-time }
JobCreateRequest:
type: object
required: [customerId, locationId, businessUnitId, jobTypeId, priority, campaignId, appointments]
properties:
customerId: { type: integer }
locationId: { type: integer }
businessUnitId: { type: integer }
jobTypeId: { type: integer }
priority: { type: string }
campaignId: { type: integer }
appointments:
type: array
items:
type: object
properties:
start: { type: string, format: date-time }
end: { type: string, format: date-time }
arrivalWindowStart: { type: string, format: date-time }
arrivalWindowEnd: { type: string, format: date-time }
technicianIds:
type: array
items: { type: integer }
summary: { type: string }
shouldUpdateInvoiceItems: { type: boolean }
JobUpdateRequest:
type: object
properties:
summary: { type: string }
priority: { type: string }
businessUnitId: { type: integer }
jobTypeId: { type: integer }
campaignId: { type: integer }
JobPagedResponse:
type: object
properties:
page: { type: integer }
pageSize: { type: integer }
hasMore: { type: boolean }
data:
type: array
items: { $ref: '#/components/schemas/Job' }
JobHistory:
type: object
properties:
data:
type: array
items:
type: object
properties:
id: { type: integer }
eventType: { type: string }
date: { type: string, format: date-time }
employeeId: { type: integer, nullable: true }
usedSchedulingTool: { type: string, nullable: true }
usedSchedulingToolVersion: { type: string, nullable: true }
Appointment:
type: object
properties:
id: { type: integer, format: int64 }
jobId: { type: integer, format: int64 }
appointmentNumber: { type: string }
start: { type: string, format: date-time }
end: { type: string, format: date-time }
arrivalWindowStart: { type: string, format: date-time }
arrivalWindowEnd: { type: string, format: date-time }
status: { type: string, enum: [Scheduled, Dispatched, InProgress, Hold, Done, Canceled] }
specialInstructions: { type: string, nullable: true }
createdOn: { type: string, format: date-time }
modifiedOn: { type: string, format: date-time }
customerId: { type: integer }
unused: { type: boolean }
AppointmentCreateRequest:
type: object
required: [jobId, start, end]
properties:
jobId: { type: integer }
start: { type: string, format: date-time }
end: { type: string, format: date-time }
arrivalWindowStart: { type: string, format: date-time }
arrivalWindowEnd: { type: string, format: date-time }
technicianIds:
type: array
items: { type: integer }
specialInstructions: { type: string }
AppointmentUpdateRequest:
type: object
properties:
specialInstructions: { type: string }
AppointmentPagedResponse:
type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/Appointment' }
Project:
type: object
properties:
id: { type: integer, format: int64 }
number: { type: string }
name: { type: string }
summary: { type: string }
status: { type: string }
statusId: { type: integer, nullable: true }
substatusId: { type: integer, nullable: true }
customerId: { type: integer }
locationId: { type: integer }
projectManagerIds:
type: array
items: { type: integer }
businessUnitIds:
type: array
items: { type: integer }
startDate: { type: string, format: date, nullable: true }
targetCompletionDate: { type: string, format: date, nullable: true }
actualCompletionDate: { type: string, format: date, nullable: true }
modifiedOn: { type: string, format: date-time }
ProjectCreateRequest:
type: object
required: [customerId, locationId]
properties:
name: { type: string }
summary: { type: string }
customerId: { type: integer }
locationId: { type: integer }
projectManagerIds:
type: array
items: { type: integer }
startDate: { type: string, format: date }
targetCompletionDate: { type: string, format: date }
ProjectPagedResponse:
type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/Project' }
JobType:
type: object
properties:
id: { type: integer }
name: { type: string }
businessUnitIds:
type: array
items: { type: integer }
skills:
type: array
items: { type: string }
tagTypeIds:
type: array
items: { type: integer }
priority: { type: string }
duration: { type: integer, description: Duration in minutes }
soldThreshold: { type: number, nullable: true }
class: { type: string }
summary: { type: string }
noCharge: { type: boolean }
enforceRecurringServiceEventSelection: { type: boolean }
invoiceSignaturesRequired: { type: boolean }
modifiedOn: { type: string, format: date-time }
externalData:
type: array
items: { type: object }
JobTypePagedResponse:
type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/JobType' }