ServiceTitan CRM API

Manage residential and commercial customer records, locations, contacts, leads, bookings, tags, and booking provider sessions. The CRM API is the primary entry point for customer-of-record data, address geocoding, and lead-to-customer conversion flows in ServiceTitan.

OpenAPI Specification

servicetitan-crm-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: ServiceTitan CRM API
  description: |
    The CRM API provides access to ServiceTitan customer-of-record data — residential and
    commercial customers, locations, contacts, leads, bookings, tags, and booking provider
    sessions. All paths are tenant-scoped and require an OAuth 2.0 access token plus the
    ST-App-Key header.
  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/crm/v2/{tenant}
    description: Production
    variables:
      tenant:
        default: "0000000"
        description: Tenant ID assigned by ServiceTitan
  - url: https://api-integration.servicetitan.io/crm/v2/{tenant}
    description: Integration (Sandbox)
    variables:
      tenant:
        default: "0000000"
        description: Tenant ID assigned by ServiceTitan
security:
  - OAuth2: []
    AppKey: []
tags:
  - name: Customers
    description: Customer-of-record records
  - name: Locations
    description: Customer service locations and addresses
  - name: Contacts
    description: Customer contact methods (phone, email)
  - name: Leads
    description: Pre-customer lead records
  - name: Bookings
    description: Customer-initiated booking requests
  - name: Tags
    description: Tag types and customer tagging
paths:
  /customers:
    get:
      summary: List Customers
      description: Returns a paginated list of customers matching the provided filters.
      operationId: listCustomers
      tags: [Customers]
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/IncludeTotal'
        - $ref: '#/components/parameters/Ids'
        - $ref: '#/components/parameters/ModifiedBefore'
        - $ref: '#/components/parameters/ModifiedOnOrAfter'
        - $ref: '#/components/parameters/CreatedBefore'
        - $ref: '#/components/parameters/CreatedOnOrAfter'
      responses:
        '200':
          description: Paginated list of customers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerPagedResponse'
    post:
      summary: Create Customer
      description: Creates a new customer record in the tenant.
      operationId: createCustomer
      tags: [Customers]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerCreateRequest'
      responses:
        '200':
          description: Created customer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
  /customers/{id}:
    get:
      summary: Get Customer
      operationId: getCustomer
      tags: [Customers]
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Customer record
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
    patch:
      summary: Update Customer
      operationId: updateCustomer
      tags: [Customers]
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerUpdateRequest'
      responses:
        '200':
          description: Updated customer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
  /customers/{id}/contacts:
    get:
      summary: List Customer Contacts
      operationId: listCustomerContacts
      tags: [Contacts]
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: List of customer contacts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactPagedResponse'
    post:
      summary: Create Customer Contact
      operationId: createCustomerContact
      tags: [Contacts]
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactCreateRequest'
      responses:
        '200':
          description: Created contact
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
  /locations:
    get:
      summary: List Locations
      operationId: listLocations
      tags: [Locations]
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/CustomerId'
        - $ref: '#/components/parameters/ModifiedOnOrAfter'
      responses:
        '200':
          description: Paginated list of locations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocationPagedResponse'
    post:
      summary: Create Location
      operationId: createLocation
      tags: [Locations]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LocationCreateRequest'
      responses:
        '200':
          description: Created location
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Location'
  /locations/{id}:
    get:
      summary: Get Location
      operationId: getLocation
      tags: [Locations]
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Location record
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Location'
    patch:
      summary: Update Location
      operationId: updateLocation
      tags: [Locations]
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LocationUpdateRequest'
      responses:
        '200':
          description: Updated location
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Location'
  /leads:
    get:
      summary: List Leads
      operationId: listLeads
      tags: [Leads]
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/Status'
        - $ref: '#/components/parameters/ModifiedOnOrAfter'
      responses:
        '200':
          description: Paginated list of leads
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeadPagedResponse'
    post:
      summary: Create Lead
      operationId: createLead
      tags: [Leads]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LeadCreateRequest'
      responses:
        '200':
          description: Created lead
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Lead'
  /leads/{id}/dismiss:
    put:
      summary: Dismiss Lead
      operationId: dismissLead
      tags: [Leads]
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Lead dismissed
  /bookings:
    get:
      summary: List Bookings
      operationId: listBookings
      tags: [Bookings]
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/ModifiedOnOrAfter'
      responses:
        '200':
          description: Paginated list of bookings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingPagedResponse'
    post:
      summary: Create Booking
      operationId: createBooking
      tags: [Bookings]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BookingCreateRequest'
      responses:
        '200':
          description: Created booking
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Booking'
  /tagtypes:
    get:
      summary: List Tag Types
      operationId: listTagTypes
      tags: [Tags]
      responses:
        '200':
          description: Tag types
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagTypePagedResponse'
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 }
    CustomerId:
      name: customerId
      in: query
      schema: { type: integer, format: int64 }
    Ids:
      name: ids
      in: query
      schema: { type: string }
      description: Comma-separated list of IDs
    Page:
      name: page
      in: query
      schema: { type: integer, default: 1, minimum: 1 }
    PageSize:
      name: pageSize
      in: query
      schema: { type: integer, default: 50, maximum: 500 }
    IncludeTotal:
      name: includeTotal
      in: query
      schema: { type: boolean }
    Status:
      name: status
      in: query
      schema: { type: string }
    ModifiedBefore:
      name: modifiedBefore
      in: query
      schema: { type: string, format: date-time }
    ModifiedOnOrAfter:
      name: modifiedOnOrAfter
      in: query
      schema: { type: string, format: date-time }
    CreatedBefore:
      name: createdBefore
      in: query
      schema: { type: string, format: date-time }
    CreatedOnOrAfter:
      name: createdOnOrAfter
      in: query
      schema: { type: string, format: date-time }
  schemas:
    Customer:
      type: object
      properties:
        id: { type: integer, format: int64 }
        active: { type: boolean }
        name: { type: string }
        type: { type: string, enum: [Residential, Commercial] }
        address: { $ref: '#/components/schemas/Address' }
        customFields:
          type: array
          items: { $ref: '#/components/schemas/CustomField' }
        balance: { type: number, format: double }
        doNotMail: { type: boolean }
        doNotService: { type: boolean }
        createdOn: { type: string, format: date-time }
        modifiedOn: { type: string, format: date-time }
        memberships:
          type: array
          items: { type: object }
        contacts:
          type: array
          items: { $ref: '#/components/schemas/Contact' }
        hasActiveMembership: { type: boolean }
    CustomerCreateRequest:
      type: object
      required: [name, type, locations]
      properties:
        name: { type: string }
        type: { type: string, enum: [Residential, Commercial] }
        doNotMail: { type: boolean }
        doNotService: { type: boolean }
        locations:
          type: array
          items: { $ref: '#/components/schemas/LocationCreateRequest' }
        contacts:
          type: array
          items: { $ref: '#/components/schemas/ContactCreateRequest' }
        customFields:
          type: array
          items: { $ref: '#/components/schemas/CustomField' }
    CustomerUpdateRequest:
      type: object
      properties:
        name: { type: string }
        type: { type: string }
        doNotMail: { type: boolean }
        doNotService: { type: boolean }
        customFields:
          type: array
          items: { $ref: '#/components/schemas/CustomField' }
    CustomerPagedResponse:
      type: object
      properties:
        page: { type: integer }
        pageSize: { type: integer }
        hasMore: { type: boolean }
        totalCount: { type: integer, nullable: true }
        data:
          type: array
          items: { $ref: '#/components/schemas/Customer' }
    Location:
      type: object
      properties:
        id: { type: integer, format: int64 }
        customerId: { type: integer, format: int64 }
        active: { type: boolean }
        name: { type: string }
        address: { $ref: '#/components/schemas/Address' }
        zoneId: { type: integer, format: int64 }
        taxZoneId: { type: integer, format: int64, nullable: true }
        createdOn: { type: string, format: date-time }
        modifiedOn: { type: string, format: date-time }
    LocationCreateRequest:
      type: object
      required: [address]
      properties:
        name: { type: string }
        address: { $ref: '#/components/schemas/Address' }
        zoneId: { type: integer }
    LocationUpdateRequest:
      type: object
      properties:
        name: { type: string }
        address: { $ref: '#/components/schemas/Address' }
        zoneId: { type: integer }
    LocationPagedResponse:
      type: object
      properties:
        page: { type: integer }
        pageSize: { type: integer }
        hasMore: { type: boolean }
        data:
          type: array
          items: { $ref: '#/components/schemas/Location' }
    Contact:
      type: object
      properties:
        id: { type: integer, format: int64 }
        type: { type: string, enum: [Phone, MobilePhone, Email, Fax] }
        value: { type: string }
        memo: { type: string, nullable: true }
        modifiedOn: { type: string, format: date-time }
    ContactCreateRequest:
      type: object
      required: [type, value]
      properties:
        type: { type: string }
        value: { type: string }
        memo: { type: string }
    ContactPagedResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Contact' }
    Lead:
      type: object
      properties:
        id: { type: integer, format: int64 }
        status: { type: string, enum: [Open, Converted, Dismissed] }
        customerId: { type: integer, format: int64, nullable: true }
        locationId: { type: integer, format: int64, nullable: true }
        businessUnitId: { type: integer, format: int64, nullable: true }
        jobTypeId: { type: integer, format: int64, nullable: true }
        priority: { type: string }
        summary: { type: string }
        callReasonId: { type: integer, nullable: true }
        campaignId: { type: integer, nullable: true }
        followUpDate: { type: string, format: date-time, nullable: true }
        createdOn: { type: string, format: date-time }
        modifiedOn: { type: string, format: date-time }
    LeadCreateRequest:
      type: object
      required: [summary]
      properties:
        customerId: { type: integer, nullable: true }
        locationId: { type: integer, nullable: true }
        businessUnitId: { type: integer }
        jobTypeId: { type: integer }
        summary: { type: string }
        priority: { type: string }
        campaignId: { type: integer }
    LeadPagedResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Lead' }
    Booking:
      type: object
      properties:
        id: { type: integer, format: int64 }
        source: { type: string }
        name: { type: string }
        address: { $ref: '#/components/schemas/Address' }
        customerType: { type: string }
        start: { type: string, format: date-time }
        summary: { type: string }
        campaignId: { type: integer, nullable: true }
        businessUnitId: { type: integer, nullable: true }
        isFirstTimeClient: { type: boolean }
        uploadedImages:
          type: array
          items: { type: string }
        status: { type: string, enum: [Pending, Converted, Dismissed] }
        modifiedOn: { type: string, format: date-time }
    BookingCreateRequest:
      type: object
      required: [name, summary, start]
      properties:
        source: { type: string }
        name: { type: string }
        address: { $ref: '#/components/schemas/Address' }
        customerType: { type: string }
        start: { type: string, format: date-time }
        summary: { type: string }
        campaignId: { type: integer }
        businessUnitId: { type: integer }
    BookingPagedResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Booking' }
    TagType:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        color: { type: string }
        active: { type: boolean }
    TagTypePagedResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/TagType' }
    Address:
      type: object
      properties:
        street: { type: string }
        unit: { type: string, nullable: true }
        city: { type: string }
        state: { type: string }
        zip: { type: string }
        country: { type: string }
        latitude: { type: number, nullable: true }
        longitude: { type: number, nullable: true }
    CustomField:
      type: object
      properties:
        typeId: { type: integer }
        name: { type: string }
        value: { type: string }