CRM Integrations (Jobber & Housecall Pro)

Deskly ships an OAuth connect scaffold for two field-service CRMs — Jobber and Housecall Pro — as groundwork for eventually letting a bot write appointments directly onto a business's real calendar. Read this page before wiring these into a customer-facing flow: part of this is real, part of it deliberately throws an error.

What's real

  • OAuth endpoints — verified against each provider's public developer docs:
    • Jobber: authorization-code grant with PKCE, https://api.getjobber.com/api/oauth/{authorize,token}
    • Housecall Pro: https://api.housecallpro.com/oauth/{authorize,token}
  • The connect/callback flowGET /api/crm/[provider]/connect and GET /api/crm/[provider]/callback implement a complete, working OAuth round trip: CSRF-safe state, PKCE for Jobber, short-lived httpOnly cookies, and a token exchange that stores access_token / refresh_token / expires_at in the crm_connections table.
  • The crm_connections table — one row per (bot_id, provider), with no client-facing select policy on the token columns (only a delete policy for disconnecting), so tokens never round-trip to the browser.

If you connect a real Jobber or Housecall Pro developer account, the connect flow will genuinely authenticate and store working tokens.

What's stubbed

Appointment creation is not implemented. createAppointment() on both createJobberClient() and createHousecallProClient() throws immediately:

async createAppointment(_input: CreateAppointmentInput) {
  throw new Error(
    "Jobber createAppointment is not implemented — verify the scheduling " +
    "mutation against a real sandbox account first"
  );
}

This is deliberate, not an oversight. Jobber's appointment/visit creation is GraphQL-based and Housecall Pro's is REST-based, and neither mutation/endpoint shape was verified against a real sandbox account when this scaffold was written — guessing at the shape would be worse than an explicit, loud failure. The CreateAppointmentInput type itself is documented as "a guess at what any field-service CRM needs," not a confirmed contract.

Before you rely on this in production

  1. Register a real developer app with Jobber and/or Housecall Pro and get sandbox credentials
  2. Open GraphiQL (Jobber) or the REST docs (Housecall Pro) against that sandbox and find the actual mutation/endpoint for creating a scheduled visit or job
  3. Confirm the X-JOBBER-GRAPHQL-VERSION header value against Jobber's current dated API version — the scaffold has a placeholder that is explicitly flagged as unconfirmed
  4. Replace the throw in createAppointment() with a real call using the confirmed shape
  5. Update CreateAppointmentInput if the real API needs different fields

Related