Three ways to connect
- Excel: Units tab, download the template, fill it in, upload it. No IT needed.
- ServiceM8: paste your ServiceM8 API key in Settings. Units and staff sync in, and bookings are written back onto the calendar.
- Anything else: this API plus webhooks. Works from your own code or from Zapier / Make.
Authentication
The owner creates a key in Settings > Connect another system. Send it on every request. Keys are shown once, can be revoked at any time, and only ever reach your own company's data. Limit: 180 requests a minute.
Authorization: Bearer sp_live_…
Endpoints
Units (installed systems)
GET /api/v1/units
POST /api/v1/units
{"units": [{"job_no": "AC-1001", "customer": "Jane Citizen", "email": "jane@example.com",
"phone": "0411 222 333", "address": "12 Smith St, Parramatta NSW 2150",
"unit_type": "split", "model": "Daikin 7.1kW", "installed": "2024-03-01",
"interval_months": 12, "last_service": "2025-03-04"}]}
Same job_no = update. unit_type is split or ducted. Returns counts and any row errors.
Technicians
GET /api/v1/technicians
POST /api/v1/technicians
{"technicians": [{"name": "Ryan West", "phone": "0400 111 222", "address": "8 Depot Rd, Blacktown NSW 2148",
"skills": "split, ducted", "days": "Mon-Fri", "start": "7:00", "finish": "15:30"}]}
Send a booking link
POST /api/v1/booking-links
{"job_no": "AC-1001", "kind": "service", "send": true}
→ {"url": "https://…/b/…", "sent": true}
kind: service, repair or breakdown. "send": false returns the link for you to send yourself.
Ask for a booking (e.g. the client rang you)
POST /api/v1/requests
{"job_no": "AC-1001", "kind": "repair", "offers": [["2026-12-01", "9-12"], ["2026-12-03", "any"]], "notes": "E5 error"}
→ {"request_id": 42, "best": {"tech": "Ryan West", "date": "2026-12-01", "arrival_txt": "9am", "star": true, …}}
Windows: 7-10, 9-12, 12-230, any. With auto-book on, a clean best pick books itself and you get booking.created.
Bookings
GET /api/v1/bookings?from=2026-12-01&to=2026-12-31
Webhooks
Set your https address in Settings and create a signing secret. Each event is POSTed as JSON. Failures are retried for about 2 days.
Events: booking.created, booking.cancelled, request.created, request.held, payment.paid.
{"id": 17, "type": "booking.created", "created": "2026-11-06T13:02:11",
"data": {"job_no": "AC-1001", "date": "2026-12-01", "start": "9am", "end": "10am",
"technician": "Ryan West", "customer": "Jane Citizen", "address": "…", "kind": "service"}}
Check the signature
Header X-SP-Signature: t=<unix time>,v1=<hex>, where v1 = HMAC-SHA256(secret, t + "." + raw body). Reject anything older than 5 minutes.
import hmac, hashlib, time
def verify(secret, raw_body, header):
parts = dict(p.split("=", 1) for p in header.split(","))
mac = hmac.new(secret.encode(), (parts["t"] + ".").encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, parts["v1"]) and abs(time.time() - int(parts["t"])) < 300