Back

Step by Step Guide to Building a ServiceTitan API Integration in Ruby

Aug 15, 20246 minute read

Introduction

Hey there, fellow Ruby enthusiast! Ready to dive into the world of ServiceTitan API integration? You're in for a treat. ServiceTitan's API is a powerful tool that can supercharge your field service management workflows. In this guide, we'll walk through building a robust integration that'll have you manipulating customer data, jobs, and invoices like a pro.

Prerequisites

Before we jump in, make sure you've got:

  • A Ruby environment (2.7+ recommended)
  • ServiceTitan API credentials (if you don't have these, reach out to their support team)
  • Your favorite HTTP client gem (we'll use faraday in this guide)

Authentication: Getting That Golden Ticket

First things first, let's get you authenticated:

require 'faraday' require 'json' class ServiceTitanAuth TOKEN_URL = 'https://auth.servicetitan.io/connect/token' def self.get_token(client_id, client_secret) response = Faraday.post(TOKEN_URL) do |req| req.headers['Content-Type'] = 'application/x-www-form-urlencoded' req.body = URI.encode_www_form({ grant_type: 'client_credentials', client_id: client_id, client_secret: client_secret, scope: 'all' }) end JSON.parse(response.body)['access_token'] end end

Pro tip: Store that token securely and implement a refresh mechanism. Your future self will thank you!

Setting Up the API Client

Now, let's create a base client to handle our API calls:

class ServiceTitanClient BASE_URL = 'https://api.servicetitan.io/v2' def initialize(access_token) @conn = Faraday.new(url: BASE_URL) do |faraday| faraday.headers['Authorization'] = "Bearer #{access_token}" faraday.headers['Content-Type'] = 'application/json' faraday.adapter Faraday.default_adapter end end def get(endpoint, params = {}) response = @conn.get(endpoint, params) handle_response(response) end # Implement post, put, delete methods similarly private def handle_response(response) case response.status when 200..299 JSON.parse(response.body) else raise "API error: #{response.status} - #{response.body}" end end end

Implementing Core API Endpoints

Let's tackle some key endpoints:

class ServiceTitanAPI < ServiceTitanClient def get_customers(params = {}) get('/customer/v2/customers', params) end def get_jobs(params = {}) get('/jpm/v2/jobs', params) end def get_invoices(params = {}) get('/accounting/v2/invoices', params) end end

Feel free to add more methods as needed. The world is your oyster!

Error Handling and Rate Limiting

ServiceTitan has rate limits, so let's be good citizens:

def handle_response(response) remaining = response.headers['X-Rate-Limit-Remaining'].to_i if remaining < 10 sleep(1) # Take a breather end # ... rest of the error handling logic end

Data Synchronization

For efficient syncing, consider using ServiceTitan's webhook feature or implement a smart polling mechanism. Here's a quick example of handling pagination:

def get_all_customers customers = [] page = 1 loop do response = get_customers(page: page, pageSize: 100) customers.concat(response['data']) break if response['data'].empty? page += 1 end customers end

Testing Your Integration

Don't forget to test! Here's a simple RSpec example:

RSpec.describe ServiceTitanAPI do let(:api) { ServiceTitanAPI.new('fake_token') } it 'fetches customers' do VCR.use_cassette('customers') do customers = api.get_customers expect(customers).to be_an(Array) expect(customers.first).to have_key('id') end end end

Best Practices and Optimization

  • Cache frequently accessed data
  • Use background jobs for heavy lifting
  • Keep your access token fresh

Conclusion

And there you have it! You've just built a solid foundation for your ServiceTitan API integration. Remember, this is just the beginning. Explore the API docs, experiment with different endpoints, and most importantly, have fun building awesome stuff!

Resources

Now go forth and code, you magnificent Ruby developer!