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.
Before we jump in, make sure you've got:
faraday
in this guide)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!
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
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!
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
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
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
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!
Now go forth and code, you magnificent Ruby developer!