CallSetter AI / Research

How CallSetter pushes leads into every home service CRM.

Full deep dive on Housecall Pro, ServiceTitan, Jobber, Workiz, FieldEdge, GoHighLevel, HubSpot, Pipedrive, Salesforce, and twelve more. API tiers, auth, exact endpoints, code samples, gotchas, customer onboarding UX, retry strategy, and the build order that gets us to market the fastest.

Research compiled 2026-05-26 5 parallel research agents 20+ CRMs analyzed

TL;DR & build order

Every home service CRM in the market falls into one of four buckets. Build native against the top three, bridge the long tail with Zapier, and start the ServiceTitan partner application today because the approval queue is the only true bottleneck.

PriorityCRMWhy nowAuthEffortPath
1Housecall Pro 40k+ SMB contractors, purpose-built Lead API, lands in Job Inbox OAuth 2.02-3 daysNative + marketplace listing
2Jobber 250k+ users, self-serve GraphQL API, auto-tags lead source OAuth 2.03-4 daysNative + app marketplace
3Workiz 120k+ phone-centric SMBs, simplest auth on the list Token + secret2 daysNative
4Zapier trigger Bridges 5000+ apps in one build. ServiceTitan, FieldEdge, Salesforce all reachable through it N/A3-5 daysPublish on Zapier Platform
5ServiceTitan 8k enterprise customers, highest ACV, Bookings API is exactly right OAuth 2.0, gated4-5 days + 4-12 wk approvalApply NOW, build later
6GoHighLevel Massive SMB adoption, unified contact API, Victor already knows the platform OAuth 2.0 (V2)2 daysNative + marketplace
7HubSpot Free tier covers huge addressable SMB market, best API on the list OAuth 2.01-2 daysNative + marketplace
Single most important action this week: file the ServiceTitan developer access request at developer.servicetitan.io/request-access. The 4 to 12 week partner approval is the long pole. Start it today even though we build it last.

Universal lead payload

Every CRM expects different field names. The clean play is to define ONE canonical CallSetter lead payload, then write thin per-CRM adapters that map it. This is the contract every integration code path consumes.

{
  "schema_version": "1.0",
  "event": "lead.qualified",
  "event_id": "uuid-v4-idempotency-key",
  "timestamp_utc": "2026-05-26T14:32:00Z",
  "callsetter_account_id": "acct_xyz",

  "call": {
    "call_id": "call_abc123",
    "direction": "inbound",
    "duration_seconds": 142,
    "started_at": "2026-05-26T14:29:38Z",
    "ended_at": "2026-05-26T14:32:00Z",
    "recording_url": "https://storage.callsetter.ai/recordings/call_abc123.mp3",
    "transcript_url": "https://storage.callsetter.ai/transcripts/call_abc123.txt",
    "transcript_summary": "No heat. Furnace 12 years old. Wants same-week appointment."
  },

  "contact": {
    "first_name": "Maria",
    "last_name": "Gonzalez",
    "phone": "+13055550182",
    "email": "maria@example.com",
    "address": {
      "street": "1420 NW 7th Ave", "city": "Miami",
      "state": "FL", "zip": "33136", "country": "US"
    }
  },

  "service_request": {
    "service_type": "HVAC",
    "sub_type": "heating_repair",
    "urgency": "same_day",
    "issue_description": "No heat. Furnace not turning on.",
    "preferred_schedule": "2026-05-27T08:00:00",
    "is_existing_customer": false
  },

  "ai_analysis": {
    "sentiment": "neutral",
    "conversion_intent_score": 0.87,
    "qualification_status": "qualified",
    "disqualification_reason": null,
    "qualifier_answers": {
      "owns_home": true,
      "issue_type": "heating_repair",
      "has_existing_service_contract": false,
      "urgency_window": "same_week"
    }
  },

  "routing": {
    "destination_crm": "housecall_pro",
    "crm_account_id": "hcp_tenant_4829",
    "assigned_to": null
  }
}

Key design decisions

  • event_id (UUID v4) is the idempotency key. Safe to retry without creating duplicate leads in the CRM.
  • recording_url and transcript_url are pre-signed time-limited URLs (24h expiry).
  • conversion_intent_score (0.0 to 1.0) lets CSRs prioritize leads in the inbox.
  • urgency enum: emergency, same_day, same_week, flexible.
  • Only qualified calls fire this webhook. Unqualified calls fire a separate low-priority event.

Per-CRM adapter pattern

Each CRM gets one adapter file. Adapter signature:

async function pushToHCP(payload) {...}
async function pushToST(payload)  {...}
async function pushToJobber(payload) {...}

Common code (validation, retry, DLQ, signature, logging) stays in the orchestrator. The adapter only knows its CRM's field shape.

Customer onboarding UX

TierMethodUsed forFriction
BestOAuth click-throughHCP, Jobber, ServiceTitan, GHL, HubSpot, PipedriveOne button, redirect, done
CommonAPI key paste with wizard + live validationWorkiz, FieldEdge, Notion, AirtableBranded wizard with "open your CRM, copy key" steps
FallbackWebhook URL pasteAnything via Zapier / Make / n8n bridgeCustomer pastes the URL into their automation tool

Recommended onboarding flow

Sign Up
  -> Choose CRM (dropdown with logos)
  -> [HCP/Jobber/GHL/HubSpot] OAuth click-through
  -> [Workiz/FieldEdge] API key paste wizard with live validation
  -> [Zapier] Embed Zapier widget with pre-built Zap template
  -> [Other] Copy webhook URL, paste into CRM or automation tool
  -> Fire a test lead
  -> Confirm lead appeared in CRM
  -> Done

The Zapier embed widget (Zapier Partner API) lets customers connect a Zap without leaving the CallSetter dashboard. Significantly reduces drop-off.


Housecall Pro

Difficulty 2/5   ~40,000 SMB contractors   MAX plan only for API

API access & plan gating

Public REST API at docs.housecallpro.com. Separate Partner Jobs API at docs.housecallpro.com/docs/partner-jobs/ for lead-source integrations specifically.

Plan gating is critical. API access locked to the MAX plan only ($329/mo, 8 users). Basic ($79/mo) and Essentials ($189/mo) have zero API access. Webhooks also MAX-only. Only Admin users can generate keys. No dedicated developer support from HCP engineers.

Authentication: two methods

Method A: API key (server-to-server, single tenant). Header format: Authorization: Token 0046421dc0ba47ec87bef0c089415380. Customer generates: HCP -> App Store -> API card -> Generate Key -> Name -> Copy. Grants access to ALL data in the account.

Method B: OAuth 2.0 (multi-tenant marketplace, REQUIRED for CallSetter scale). Authorization at pro.housecallpro.com/oauth/authorize, token exchange at api.housecallpro.com/oauth/token.

OAuth gate: To get CLIENT_ID + CLIENT_SECRET, email apideveloper@housecallpro.com with your CALLBACK_URL. Manual approval, 1 to 5 business days. Not self-serve. Start this email today.

Endpoints

Base: https://api.housecallpro.com/v1/

EndpointUseNotes
POST /customersCreate customerNo auto-dedup. Search by phone first.
POST /jobsCreate jobRequires existing customer_id + address_id.
POST /leads   USE THISCreate leadLands in Job Inbox -> API Leads channel. Dispatcher reviews + converts. Matches existing workflow.
POST /jobs/{id}/notesAttach transcriptRecording URL embedded as link.
POST /job_linksNo-auth share URLDrops a lead into an HCP account without needing API key. Investigate for non-MAX customers.

Reference implementation

const HCP_BASE = 'https://api.housecallpro.com/v1';
const headers = { 'Authorization': `Token ${token}`, 'Content-Type': 'application/json' };

async function pushLeadToHCP(payload) {
  // 1. Dedup check by phone
  const r = await fetch(`${HCP_BASE}/customers?mobile_number=${encodeURIComponent(payload.contact.phone)}`, { headers });
  const { customers } = await r.json();

  let customerId;
  if (customers?.length > 0) {
    customerId = customers[0].id;
  } else {
    // 2. Create customer
    const c = await fetch(`${HCP_BASE}/customers`, {
      method: 'POST', headers,
      body: JSON.stringify({
        first_name: payload.contact.first_name,
        last_name: payload.contact.last_name,
        mobile_number: payload.contact.phone,
        email: payload.contact.email,
        address: { ...payload.contact.address, country: 'US' },
        tags: ['callsetter-ai'],
        notifications_enabled: true,
      }),
    }).then(r => r.json());
    customerId = c.id;
  }

  // 3. Create LEAD (Job Inbox -> API Leads channel)
  return fetch(`${HCP_BASE}/leads`, {
    method: 'POST', headers,
    body: JSON.stringify({
      customer_id: customerId,
      name: `${payload.contact.first_name} ${payload.contact.last_name}`,
      phone_number: payload.contact.phone,
      description: `[CallSetter AI] ${payload.service_request.service_type} | Urgency: ${payload.service_request.urgency}\nRecording: ${payload.call.recording_url}`,
      lead_source: 'CallSetter AI',
    }),
  }).then(r => r.json());
}

Webhooks (MAX only)

Events: customer.*, job.created/scheduled/started/on_my_way/completed/canceled/paid, estimate.*, lead.created/updated/converted/lost/deleted, pro_created.

For CallSetter, the high-value subscriptions are lead.converted (closes the attribution loop), job.scheduled (trigger confirmation SMS), job.completed + job.paid (revenue attribution).

Webhook payload note: includes event type + resource ID, NOT the full record. Follow up with GET /jobs/{id} for state. HMAC-SHA256 signed.

Marketplace listing

HCP integrations page lists ~30 partners (Thumbtack, CompanyCam, CallRail, Angi). Path: same apideveloper@housecallpro.com email. Relationship-driven, no self-serve form. CallSetter's profile (AI voice agent for HVAC/plumbing/roofing) is a clean fit next to CallRail and Angi.


ServiceTitan

Difficulty 3/5   ~8,000 enterprise contractors   Partner approval gated

Two API access paths

Custom Integration (single-tenant): contractor admin -> Settings -> Integrations -> API Application Access -> Connect New App -> approve scopes -> generates Client ID + Client Secret. <20 min. No marketplace listing required. Use this for first 10-20 contractors.

Marketplace (multi-tenant): required for CallSetter at scale. Vendor registers once at developer.servicetitan.io, obtains App Key. Three certification tiers (Silver / Gold / Titanium), 5-stage process, annual dues, annual re-cert. 4 to 12 week approval timeline. Contact: integrations@servicetitan.com.

Authentication

OAuth 2.0 client credentials grant (machine-to-machine, no user login). No refresh tokens. Tokens expire in 900 sec (15 min). Cache per-tenant or get throttled.

EnvAuth URLAPI Base
Integration (sandbox)auth-integration.servicetitan.io/connect/tokenapi-integration.servicetitan.io
Productionauth.servicetitan.io/connect/tokenapi.servicetitan.io

Required on every API call: Authorization: Bearer {token} AND ST-App-Key: {app_key} (distinct from Client credentials).

Endpoints (use Bookings)

Pattern: {base}/{namespace}/v2/tenant/{tenant_id}/{resource}

EndpointUse
POST /crm/v2/tenant/{tid}/bookings PRIMARYLands on Calls screen for CSR. Min: name + (address/phone/email). Pre-fill jobTypeId, businessUnitId, campaignId, source, summary.
POST /crm/v2/tenant/{tid}/customersCustomer record. Required: name, type, contact.
POST /crm/v2/tenant/{tid}/locationsJob site address.
POST /jpm/v2/tenant/{tid}/jobsJob. Required: customerId, locationId, jobTypeId, businessUnitId, priority.
POST /jpm/v2/tenant/{tid}/appointmentsScheduled time slot.
Booking Provider Tag prerequisite: contractor must create a tag in Settings -> Integrations -> Booking Provider Tags BEFORE first API call. CallSetter onboarding must verify this exists. Set "restriction by booking provider" to No Restriction unless tight single-source.

Reference implementation

# Get access token
TOKEN=$(curl -s -X POST "https://auth.servicetitan.io/connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=${ST_CLIENT_ID}&client_secret=${ST_CLIENT_SECRET}" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# Create booking from CallSetter
curl -s -X POST "https://api.servicetitan.io/crm/v2/tenant/${TENANT_ID}/bookings" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "ST-App-Key: ${ST_APP_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "CallSetter AI",
    "name": "John Martinez",
    "address": { "street": "4521 Oak Ridge Dr", "city": "Phoenix", "state": "AZ", "zip": "85016", "country": "USA" },
    "contacts": [
      { "type": "Phone", "value": "+16025551234", "memo": "Mobile" },
      { "type": "Email", "value": "john.martinez@gmail.com" }
    ],
    "jobTypeId": 12345,
    "businessUnitId": 67890,
    "campaignId": 11111,
    "summary": "AC not cooling. 3-ton Carrier 2019. Recording: https://storage.callsetter.ai/rec/abc123.mp3",
    "isFirstTimeClient": true,
    "bookingProviderId": 99
  }'

Webhooks V2

V1 sunsetting. V2 at developer-next.servicetitan.io/docs/webhooks. Subscribe via POST /webhooks/v2/register. HMAC-SHA256 verification via x-servicetitan-signature header. Exponential backoff, up to 3 retries. Ack with 200 immediately, process async.

Rate limits

60 calls/sec per app per tenant. Reporting APIs 5/min same report. CallSetter's 3-5 calls per completed call is well below. Token requests count toward limit, so cache per-tenant.

The #1 gotcha: tenant-specific enum IDs

businessUnitId, jobTypeId, campaignId, bookingProviderId are integer IDs unique PER contractor. "Residential HVAC" might be 12345 on one tenant, 98765 on another. Onboarding must query and store the mapping:

GET /settings/v2/tenant/{tid}/business-units
GET /settings/v2/tenant/{tid}/job-types
GET /settings/v2/tenant/{tid}/campaigns
GET /crm/v2/tenant/{tid}/booking-provider-tags

Present these as dropdowns during CallSetter onboarding. Mismatched jobType / businessUnit pair causes CSR dropdown to silently fail to pre-fill.


Jobber

Difficulty 2/5   ~250,000 pros   Connect plan+ for API

API type

GraphQL API at https://api.getjobber.com/api/graphql (single endpoint, no REST). Version pinned per-request via X-JOBBER-GRAPHQL-VERSION: 2023-11-15 header. Available on Connect plan and above (not Core). 90-day free dev trial.

Authentication: OAuth 2.0

Authorization Code Grant. No API keys. Access tokens expire in 60 min, refresh tokens long-lived. Scopes for CallSetter: clients:read/write, jobs:read/write, requests:read/write, quotes:read/write.

Mutations

mutation CreateClient($input: ClientCreateInput!) {
  clientCreate(input: $input) {
    client { id firstName lastName phones { number primary } }
    userErrors { message path }
  }
}

# Variables
{
  "input": {
    "firstName": "Maria", "lastName": "Gonzalez",
    "phones": [{ "number": "+13055550182", "description": "MAIN", "primary": true }],
    "emails": [{ "address": "maria@example.com", "description": "MAIN", "primary": true }],
    "address": {
      "street1": "1420 NW 7th Ave", "city": "Miami",
      "province": "FL", "postalCode": "33136"
    },
    "notes": "Inbound call via CallSetter AI. Service: HVAC repair. Recording: https://..."
  }
}
Auto-attribution win: as of Sept 16, 2024, Jobber automatically captures your app's name in the "Lead Source" field on every client created via clientCreate. Cannot be edited by Jobber users. CallSetter AI gets credit for every lead it pushes, built in.

For the service request itself, follow with requestCreate (matches Jobber's Requests pipeline stage, the intake step before Quote/Job) OR jobCreate (skip the request stage, go straight to a scheduled job).

Webhooks

App-level (not per-account). Must ack within 1 second. At-least-once delivery (use itemId for idempotency). HMAC-SHA256 signed via X-Jobber-Hmac-SHA256 header.

Topics: CLIENT_CREATE/UPDATE/DESTROY, REQUEST_CREATE, QUOTE_CREATE, QUOTE_APPROVAL, JOB_COMPLETE, INVOICE_CREATE, APP_CONNECT.

Rate limits

Per app / per Jobber account: DDoS middleware 2500 req/5 min (HTTP 429). GraphQL cost bucket: 10000 points max, 500/sec restored. CallSetter's per-call cost well under 100 points.

App Marketplace

Three-stage: (1) Draft state allows up to 5 paying Jobber accounts without review (covers initial pilots), (2) App review submitted via Developer Center, (3) ~2-week beta with selected customers, then published. 2FA required on developer account.


Workiz

Difficulty 2/5   ~120,000 customers   All plans


FieldEdge

Difficulty 4/5   ~5,000 customers   Partner-gated API

Owned by Xplor Technologies (acquired 2021). API exists at docs.api.fieldedge.com (Azure API Management) but is gated to "integrated partners" only. No self-serve.

Pragmatic path: ship via Zapier today (native FieldEdge connector with "Create Work Order" action), submit partner application in parallel at fieldedge.com/partners for official API access later.

Known endpoint patterns (partner portal): POST /sessions, GET /customers, POST /work-orders, PATCH /work-orders/{id}/status, GET /appointments. No public webhooks. Customer creation typically a side-effect of work-order creation.


GoHighLevel (V2)

Difficulty 2/5   Massive SMB home services   V1 sunset 2025-12-31


HubSpot

Difficulty 1/5 (easiest on the list)   Free tier API access

Best API on this entire list. Atomic contact creation, robust webhooks on free tier, well-documented. Highest leverage build per dev-day spent.


Pipedrive

Difficulty 2/5   All plans including Essential


Other CRMs (matrix)

CRMAPIAuthLead createWebhooksZapierDifficulty
Service FusionREST, Pro plan+OAuth 2.0 Client Credentials POST /customers then POST /jobsNoYes3/5
mHelpDeskRESTHTTP Basic POST /api/v1/customers, /jobsYesYes3/5
BookingKoala (cleaning)REST, partialAPI key Bearer Unconfirmed inbound endpointOutbound onlyUnconfirmed actions4/5
Zoho CRMREST, Standard+OAuth 2.0 (DC-aware) POST /crm/v8/Leads via Zoho-oauthtoken headerYesYes3/5
SalesforceRESTOAuth 2.0 Connected App POST /sobjects/Lead/ (Company REQUIRED)SOAP / Platform Events / CDCYes4/5
Monday.comGraphQLOAuth 2.0 Bearer create_item mutation (board_id + column schema per customer)YesYes3/5
PipefyGraphQLOAuth 2.0 / Service Account createCard mutation (pipe_id + field_ids per customer)YesYes3/5
AirtableRESTPAT Bearer or OAuth 2.0 POST /v0/{baseId}/{tableId}Yes (since 2022)Yes2/5
NotionRESTIntegration Token or OAuth 2.0 POST /v1/pages with database_id parentNo (poll)Yes (poll)3/5
Service AutopilotClosedN/A No public pathNoNo5/5 (manual)
ResponsiBidOutbound onlyN/A No inbound path (ResponsiBid is the source, not the destination)Outbound onlyOutbound onlyN/A

iPaaS: Zapier, Make.com, n8n

Why publish a Zapier app at all

Even with native HCP / Jobber / Workiz adapters, the long tail of CRMs (Salesforce, Zoho, Monday, Notion, Pipedrive, Service Fusion, FieldEdge, mHelpDesk, etc.) is best reached via Zapier. One Zapier app = 5,000+ destinations.

What to build on Zapier Platform

Publishing path

  1. Register at zapier.com/developer-platform/integrations
  2. Choose Platform UI (no-code, browser) for speed OR Platform CLI (zapier-platform-cli npm) for version control
  3. Define trigger as REST Hook
  4. Submit -> Zapier reviews ~1 week -> 90-day Beta phase -> Public. Early exit: one signup via embedded widget unlocks Public immediately.

Zapier vs Make.com

FactorZapierMake.com
App directory~5,000~2,000
Customer cost$30-70/mo~$10/mo
LatencyREST Hook instantWebhook instant
Learning curveLowMedium
Best forEnterprise / mid-marketSMB

Build Zapier first. Add Make.com as the second option once Zapier ships. Surface both in onboarding.

n8n self-host (V2 or later)

Appropriate at 100+ customers when Zapier/Make per-customer fees or brittleness become measurable. CallSetter runs n8n internally, each customer = a workflow, CallSetter posts to https://n8n.callsetter.ai/webhook/{customer-id}. Full delivery visibility. Not the MVP choice.


Retry & dead-letter queue

Never drop a lead because the customer's CRM was down at 2 AM.

Retry schedule (exponential backoff with jitter)

AttemptWait
1st retry30s ± 10s
2nd retry2m ± 20s
3rd retry10m ± 1m
4th retry1h ± 10m
5th retry6h ± 30m
Give up-> DLQ

HTTP 4xx errors -> straight to DLQ (auth/endpoint bad, not retriable). Only 5xx and timeouts retry.

DLQ preserves

Full original payload, all request headers, HTTP status code + error body per attempt, retry count, timestamps per attempt, event_id (idempotent replay).

DLQ management

Production-ready V1 shortcut: Hookdeck.com is purpose-built webhook delivery with DLQ, retry, and replay built in. ~$25/mo for 250,000 events/mo. Drop-in solution before building custom queue infrastructure.

Two-way sync (V2)

V1 = one-way push (CallSetter -> CRM). Covers 95% of customer value. Simple, debuggable, no inbound API surface to secure. Build first.

Two-way matters for two scenarios:

  1. Job Won callback. CRM marks job invoiced -> push revenue back to CallSetter -> closes attribution loop -> powers "calls-to-revenue" reporting in CallSetter dashboard. High product value. Medium engineering cost.
  2. Booking confirmed. CRM books slot -> update CallSetter call record -> follow-up calls have context.

Cost: roughly 3-5x V1 effort (per-CRM inbound webhook, signature verification, internal record updates).

Verdict: ship V1 one-way first. Build Job Won callback in V2 when customers start asking "did my calls turn into jobs?"


Immediate actions

This week

  1. Apply for ServiceTitan developer access at developer.servicetitan.io/request-access. 4-12 wk approval. Start the slot today.
  2. Email apideveloper@housecallpro.com requesting OAuth credentials + technology partner listing conversation. Send your callback URL. 1-5 business days.
  3. Email integrations@servicetitan.com to open Silver-tier marketplace conversation in parallel with the dev access request.
  4. Lock the universal lead payload schema (above). Every adapter consumes this contract.

Build order (weeks 1-4)

  1. Lock universal lead payload schema + Postgres outbox + retry orchestrator + DLQ (or wire Hookdeck for V1 speed).
  2. HCP native adapter via Lead endpoint + OAuth. Dogfood on 1-2 friendly contractor pilots.
  3. Jobber native adapter via clientCreate + requestCreate. App registered on Jobber Developer Center, Draft state pilots.
  4. Workiz native adapter (simplest auth, fastest to ship).
  5. Zapier app published with REST Hook trigger.
  6. HubSpot + GoHighLevel native adapters (free tiers = huge addressable market).

Build order (weeks 5-12)

  1. Pipedrive native adapter.
  2. ServiceTitan native (after partner approval lands).
  3. Salesforce + Zoho via Zapier (avoid the OAuth + per-org instance complexity for V1).
  4. Customer onboarding UX polish: OAuth click-throughs, API key wizard, Zapier embed widget.
  5. Two-way V2: Job Won callback from HCP, Jobber, ServiceTitan.
  6. Marketplace listings: Jobber published state, HCP integrations page, ServiceTitan Silver certification.