Developers
Connect your other tools
1. Make an API key
In the app, go to Settings, Connections (the business owner only). Give the key a name, tick only what it needs to do, and tap Make a key. Copy the key straight away: it starts with sb_live_ and you only see it once. We keep a fingerprint of it, not the key itself.
Each key does only what you ticked:
read:jobs: See jobswrite:jobs: Add jobsread:clients: See clientswrite:clients: Add clientsread:quotes: See quotes and their totalsread:invoices: See invoices and amountswrite:notes: Add notes to jobsread:schedule: See the schedule (visits)
Money (quote totals, invoice amounts) only ever comes back with read:quotes or read:invoices. A key can never change your settings, staff or billing. Revoke a key in the same place and it stops working at once. Keys also stop working if the owner who made them is no longer an owner of the business.
2. The REST API
Every request sends the key as a bearer token. Answers are JSON. Money is in whole cents (12350 is $123.50, GST included), dates are YYYY-MM-DD and times are ISO 8601 in UTC.
curl https://sparkybuddy.com.au/api/v1/jobs?status=booked \
-H "Authorization: Bearer sb_live_your_key"Lists come back a page at a time, newest first. Pass the next_cursor you got back to get the next page (it's null on the last):
{
"data": [ { "id": "…", "number": 1042, "title": "Replace switchboard", "status": "booked", … } ],
"next_cursor": "WzEwNDJd"
}
curl "https://sparkybuddy.com.au/api/v1/jobs?limit=50&cursor=WzEwNDJd" -H "Authorization: Bearer sb_live_your_key"| Request | Needs | What it does |
|---|---|---|
| GET /clients | read:clients | Your clients, newest first. ?q= searches name, business, email and phone. |
| POST /clients | write:clients | Add a client: name, and any of kind, business_name, email, phone, billing_address, notes. |
| GET /jobs | read:jobs | Jobs, newest first. ?status=, ?client_id= and ?q= to narrow it. |
| POST /jobs | write:jobs | Add a job for client_id, or with a new client (also needs write:clients). Optional first visit. |
| GET /jobs/{id} | read:jobs | One job. Its quotes come too with read:quotes, its invoices with read:invoices. |
| POST /jobs/{id}/notes | write:notes | Add a dated note to the end of the job's notes. Never replaces what's there. |
| GET /quotes | read:quotes | Quotes with totals. ?status= (draft, sent, accepted, declined) and ?job_id=. |
| GET /invoices | read:invoices | Invoices with amounts paid and owing. ?status= (draft, sent, paid, void) and ?job_id=. |
| GET /schedule?from=&to= | read:schedule | Visits between two dates (YYYY-MM-DD, up to 62 days), in order. |
Add a job with a new client and a first visit:
curl -X POST https://sparkybuddy.com.au/api/v1/jobs \
-H "Authorization: Bearer sb_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"title": "Install two power points",
"site_address": "12 Example St, Southport QLD 4215",
"client": { "name": "Alex Sample", "phone": "0491 570 006" },
"scheduled_for": "2026-11-03",
"start_time": "08:00",
"duration_minutes": 120
}'Add a note to a job:
curl -X POST https://sparkybuddy.com.au/api/v1/jobs/JOB_ID/notes \
-H "Authorization: Bearer sb_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "text": "Customer rang: side gate code is 1234" }'3. Webhooks
Instead of asking us over and over, let us tell you. In Settings, Connections, add the https address your tool gave you (in Zapier, a "Catch Hook"; in n8n or Make, a Webhook trigger) and tick the events you want:
job.created: A job is createdjob.status_changed: A job moves to a new statusquote.accepted: A customer accepts a quoteinvoice.paid: An invoice is paid in fullenquiry.received: A new enquiry comes invisit.booked: A visit is booked on the schedule
Each one is a POST with a JSON body like this. "Send a test event" sends a ping, so your tool has a sample to map.
{
"id": "6f1c…",
"type": "invoice.paid",
"created_at": "2026-10-26T03:12:45.000Z",
"data": {
"invoice": { "id": "…", "number": 207, "job_id": "…", "total_cents": 48400, "paid_on": "2026-10-26", "payment_method": "card" }
}
}Checking it came from us. Each webhook is signed the Standard Webhooks way, with the signing secret shown when you added it (it starts whsec_). Three headers come with it: webhook-id, webhook-timestamp and webhook-signature. The signature is v1, then the base64 HMAC-SHA256 of id.timestamp.body, keyed with the secret's bytes (the part after whsec_, base64-decoded). Reject it if the timestamp is more than 5 minutes old.
// Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function fromUs(secret, headers, rawBody) {
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const signed = `${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${rawBody}`;
const expected = "v1," + createHmac("sha256", key).update(signed).digest("base64");
const fresh = Math.abs(Date.now() / 1000 - Number(headers["webhook-timestamp"])) < 300;
return fresh && headers["webhook-signature"].split(" ").some(
(s) => s.length === expected.length && timingSafeEqual(Buffer.from(s), Buffer.from(expected)));
}Answer with any 2xx status within 10 seconds. If you don't, we try again later (after 1 minute, 5 minutes, 30 minutes, then hours apart), 8 tries in all over about two days, with the same webhook-id so you can ignore repeats. We don't follow redirects. After 15 failures in a row the webhook is switched off and the owner is told; switch it back on in Settings. The last 20 deliveries for each webhook are listed there.
4. ChatGPT, Claude and other AI assistants
SparkyBuddy has an MCP server, the standard way AI assistants connect to apps. Point your assistant at this address and give it an API key as the bearer token:
https://sparkybuddy.com.au/api/mcpIt offers these tools, each only if the key has the matching box ticked:
find_jobsandget_job(read:jobs)find_clients(read:clients)list_schedule(read:schedule)add_job_note(write:notes)create_job(write:jobs, and write:clients for a new client)
It works with assistants and tools that let you set a bearer token on an MCP connection: Claude Code, the Claude and OpenAI APIs' MCP connectors, n8n's MCP client and others. For example, in Claude Code:
claude mcp add --transport http jobs https://sparkybuddy.com.au/api/mcp \
--header "Authorization: Bearer sb_live_your_key"Make a separate key for each assistant, and only tick the write boxes if you want it to change things. Assistants can get things wrong: check what it adds.
5. Errors and limits
Errors come back as JSON with a code and a message written for people:
{ "error": { "code": "missing_scope", "message": "This key can't do that. It needs: read:invoices. …", "needed": ["read:invoices"] } }400something in the request isn't right (the message says what)401no key, a wrong key or a revoked one403the key doesn't have that box ticked, or the plan doesn't include connections404no such record in your business (or no such endpoint)429too many requests: wait for the number of seconds in theRetry-Afterheader
Each key can make 120 requests a minute, and 60 changes every 10 minutes. Pages are up to 100 records (50 if you don't say). A key only ever sees its own business's records.
