Integration recipes
Each recipe lists the scopes to request, the calls in order, and what trips integrations up. External pilot partners run them against staging only; production access isn't offered yet. The rules behind each call are in the API guide; shared conventions are in the quickstart.
Website booking
A booking page that prices a clean, saves the quote, creates the customer and books the first visit.
Scopes: pricing.read, quotes.write, quotes.read, customers.write, customers.read, homes.write, zones.read, availability.read, bookings.write. Add payments.write to collect a card.
GET /quotes/configonce, and cache it. Build the form from it, and use only its ids. Offer the frequencies withbookable: true, and offercustomer_sourcesas the "How did you hear about us?" choice.POST /quotes/calculateas the customer chooses. Free, saves nothing.POST /quoteswith the contact details, the chosenpricingand the chosen source assource_name. Keep the returnedidandupdated_at.PATCH /quotes/{id}for each change the customer makes, always with the latestupdated_at.- Create the customer with
POST /customers. On409 duplicate_customer, find the existing one withGET /customers/lookup?query=<email or phone>and use it. POST /customers/{customer_id}/homeswith the service address. To check the postal code first,GET /zones?include_zips=trueand match it: a postal code in no zone is refused. Keep the home'szone_id.GET /availabilitywithzone_idfrom the home andduration_hours= the quote'spricing.first_visit_minutes÷ 60, rounded up to two decimals (0.5 to 12).POST /quotes/{id}/bookwithexpected_updated_at(the quote'supdated_at), thecustomer_idandhome_idfrom steps 5 and 6, and the chosen slot'sdate, a start inside its window asarrival_window_start, andteam_id.- Optional:
POST /quotes/{id}/payment-linksafter booking, and send theurlto the customer yourself.
Gotchas.
- A frequency with
bookable: falsecan be quoted but not booked (409 quote_not_bookable). Leave it off a self-booking form. - Availability isn't held. On
409 slot_unavailable, read availability again and let the customer choose again. - A saved price expires (
expires_at). On409 quote_expired, recalculate andPATCHthe quote, then book with the newupdated_at. - Mint the payment link after booking: a link minted earlier saves the card to the prospect, not the customer.
- Use a new
Idempotency-Keyfor each distinct request (every quote revision gets its own), and reuse a key only to retry that exact request.
Lead capture and CRM sync
Send web-form and ad leads into Pulse, then keep a CRM in step.
Scopes: leads.write, leads.read, quotes.read, customers.read, webhooks.manage. Add customers.write to push contact changes back.
POST /leadswith contact details, postal code,medium, and first-touchattribution(UTM tags,gclid,fbclid). Attribution can only be set here.- Subscribe to
lead.created,lead.convertedandcustomer.created,customer.status_changed. - On
lead.converted, readGET /customers/{data.customer_id}and link it to the CRM contact. PATCH /leads/{id}orPATCH /customers/{id}for contact changes, with the latestupdated_at.
Gotchas.
- Lead writes run no duplicate check. Call
GET /customers/lookupbeforePOST /leadsif you don't want a lead for an existing customer. lead.createdalso fires for leads from Pulse's own forms and widget, and for your ownPOST /leads; deduplicate on the lead id.- A converted lead leaves
/leadsand answers404there; read it from/quotes. - Email and phone can be replaced but not cleared.
- Webhooks don't cover changes made in MaidCentral V2 (the classic app). Poll
GET /leadsandGET /customerswithupdated_since=<day of last run>to catch them, and reconcile with a periodic full read for what it can't report.
Nightly BI export
Load Pulse data into a warehouse.
Scopes: customers.read, homes.read, services.read, jobs.read, billing.read, time.read, reports.read, usage.read.
- Customers, homes and service sets: page through each list with
updated_sinceset to the day of your last run. The first run omits it. - Jobs:
GET /jobsover a rolling window (for example 30 days back to 60 ahead), with the requiredstart_dateandend_date, plusupdated_since. Load history once in ranges of up to 366 days. - Invoices and payments:
GET /invoicesandGET /paymentswithupdated_sinceset to the day of your last run. - Time:
GET /time-summaries/dailyand/jobsin ranges of up to 31 days. - Reports: one call per report per run; they don't page.
- Check
GET /usageafter the run.
Gotchas.
- Deleted records simply stop appearing, and
updated_sincedoesn't report them or a few other changes (Incremental sync). Run a full read periodically, for example weekly, and compare. - Don't store cursors between runs; they expire after 24 hours.
/invoices,/paymentsand the reports share the report rate class: 10 calls a minute. Pace them.- Each page counts one operation, so use
page_size=100. - Read
data_availableon every report before loading its numbers.
AI assistant lookup
An assistant that answers questions about a customer.
Use the MCP server; it signs in with Pulse and needs no key, and its tools cover the steps below. Build your own with an API key only when you need a custom assistant.
Scopes: customers.read, homes.read, services.read, jobs.read. Add billing.read for balances, quotes.read for open quotes.
GET /customers/lookup?query=…with an email, phone or name; up to 20 matches.GET /customers/{id},/customers/{id}/homes,/customers/{id}/service-sets.GET /customers/{id}/jobs?start_date=…&end_date=…for recent and upcoming visits.GET /customers/{id}/billingfor the balance.
Gotchas.
- Phone lookup needs the digits exactly as stored, at least 7 of them. Try without a country code first.
- Give the assistant a read-only key. Never put the key in a prompt.
- Each call counts one operation; cache within a conversation.