All documentation
Integrations & API

Connecting a CRM

Patterns for HubSpot, Pipedrive, Salesforce and Zoho — won deal in, project status back out — plus accounting, calendars and automation platforms.

Updated 2 September 2026

Nearly every CRM request comes down to two flows, in one or both directions. This page gives the shape of each, then the specifics per platform. It assumes you have read the API page.

The two flows

Deal won → project created

A deal reaches Closed Won in the CRM. Something creates the matching BuildFlow project and invites the client.

  1. 1

    Trigger

    The CRM's own webhook on deal-stage change, or a poll of deals updated since your last run.

  2. 2

    Map the fields

    Deal name → project name, deal type → project_type, the property address → address fields, expected start → start_date, value → contract_sum.

  3. 3

    Create the project

    Call create_project_from_template so stages, tasks, milestones and inspections arrive together and consistent.

  4. 4

    Write the id back

    Store the BuildFlow project id on the CRM deal in a custom field. Everything afterwards depends on this join.

  5. 5

    Invite the client

    Either through the app, or by creating an invite row and sending the link yourself.

Make it idempotent

Webhooks fire twice. Before creating, look for an existing project carrying that deal id — the reference column is there for your own job reference and works well as the join key.

Project status → back onto the CRM record

A scheduled job — hourly is plenty — reads the projects that have changed and updates the CRM.

Everything that moved in the last hour
GET /rest/v1/projects
  ?select=id,name,reference,status,completion_pct,target_end_date,updated_at
  &company_id=eq.<company-id>
  &updated_at=gte.2026-09-02T09:00:00Z

Push status, completion_pct and target_end_date onto the deal or the linked project record. That alone answers most of what a sales or office team wants to know without them logging into anything.

Field mapping

BuildFlowTypeTypical CRM field
nametextDeal / opportunity name
referencetextYour job number — best join key back to the deal id
project_typeenumextension, kitchen, bathroom, loft_conversion, garage_conversion, roofing, landscaping, renovation, new_build, custom
statusenumplanning, active, on_hold, snagging, completed, archived
address_line1, city, postcodetextSite address on the deal or the contact
start_date, target_end_datedateExpected start and completion
contract_sumnumericThe agreed sum, net of VAT. The client sees this on their Payments screen.
completion_pct0–100A progress field, or a stage picklist

HubSpot

  • Out: a workflow on Deal stage = Closed Won, with a webhook action to your endpoint. Send the deal id and the properties you need.
  • Back: create a custom deal property buildflow_project_id (single-line text) and write it on creation. Add buildflow_status and buildflow_progress for the return sync.
  • In: the CRM API v3 PATCH /crm/v3/objects/deals/{dealId} updates those properties from your scheduled job.
  • Watch for: HubSpot workflows retry, so guard against duplicates on your side.

Pipedrive

  • Out: Pipedrive webhooks on updated.deal; check the new stage yourself, since the filter is coarse.
  • Back: a custom deal field for the project id — note that Pipedrive custom fields are addressed by a hash key, not the name you typed.
  • In: PUT /v1/deals/{id} for the status sync.
  • Watch for: the webhook payload carries both current and previous; compare them so you only fire on the transition into Won, not on every edit afterwards.

Salesforce

  • Out: an Outbound Message or a Flow with an HTTP callout on Opportunity stage change. Apex triggers give more control if you already have developer support.
  • Back: a custom field BuildFlow_Project_Id__c on Opportunity.
  • In: the REST API PATCH /services/data/vXX.X/sobjects/Opportunity/{id}.
  • Watch for: Salesforce needs the callout endpoint registered in Remote Site Settings, and it will not call an endpoint without a valid certificate.

Zoho CRM

  • Out: a Workflow Rule with a Webhook action, or Deluge in a custom function if you want the logic inside Zoho.
  • Back: a custom field on Deals for the project id.
  • In: PUT /crm/v6/Deals with the record id.
  • Watch for: Zoho's OAuth tokens are region-specific — use the datacentre domain your account is on, not the .com default.

Automation platforms

If you would rather not run a service, all three of these will do the job with their generic HTTP step. There is no BuildFlow connector to click; you are configuring requests by hand.

PlatformUseNotes
ZapierWebhooks by Zapier → Custom RequestStorage by Zapier can hold the access token between runs
MakeHTTP module, plus a Data Store for the tokenIts scheduling is the most flexible of the three
n8nHTTP Request nodeSelf-hostable, so the secret key can stay on your own infrastructure

Token handling on a hosted platform

Access tokens last an hour. Either refresh at the start of each run, or store the refresh token and exchange it. Do not paste the secret key into a hosted automation platform — that hands your whole database to a third party.

Accounting and job costing

Xero, QuickBooks and Sage have no direct link, and BuildFlow holds the contract sum, the scope of works and the payment schedule, and nothing at ledger level. What is worth syncing is the other direction: project names and references, so invoices can be coded to a job that exists.

  • Create the tracking category or job in the accounts package when the BuildFlow project is created, from the same trigger.
  • Use reference as the shared key across all three systems.
  • Pull status back so finished jobs can be closed off rather than lingering.

Calendars

There is no iCal feed yet — it is on the shortlist. Until then, a scheduled job can read milestones and inspections and create events through the Google Calendar or Microsoft Graph API:

GET /rest/v1/milestones?project_id=eq.<id>&select=id,name,due_date,completed_at
GET /rest/v1/building_control_inspections
  ?project_id=eq.<id>&select=id,name,authority,scheduled_for,status

Keep the BuildFlow row id in the calendar event's extended properties so a rescheduled inspection updates the event instead of creating a second one.

Slack and Teams

Without webhooks this is a poll: every few minutes, ask for rows created since your last check and post the new ones to an incoming webhook.

New snags since the last run
GET /rest/v1/snag_items
  ?project_id=eq.<id>
  &created_at=gte.<last-run-iso>
  &select=id,title,room,status,reporter:profiles!snag_items_reported_by_fkey(full_name)
  &order=created_at.asc

Store the timestamp of the newest row you posted rather than the time you ran, or a slow write will be skipped.

Before you go live

  1. 1.Run it against a project you do not mind breaking. Every write in these recipes is real.
  2. 2.Give the integration its own user with the least role that works, so the audit trail shows what it did.
  3. 3.Handle 403 and 409 explicitly: the first means access, the second usually means a plan limit or a duplicate.
  4. 4.Log what you wrote, with the row ids. Reconstructing a bad sync without that is miserable.
  5. 5.Decide which system wins on a conflict, and write it down.

Try it on a real job

The free plan runs one project end to end — the fastest way to find out whether any of this fits how you work.

Connecting a CRM — BuildFlow documentation · BuildFlow