Connect your CRM
Send finished galleries to your CRM with Zapier or Make, or use the API directly.
Zapier
- Create a Zap. For the trigger, choose Webhooks by Zapier, then Catch Hook. Copy the URL it gives you.
- In JobProof, open Settings, paste it under Connect your CRM, and tap Send test.
- Back in Zapier, test the trigger. Add an action for your CRM, like a note on the customer with
job.shareUrl. - Filter on
eventequalsgallery.readyif you only want finished galleries.
Make
Add a Webhooks module with Custom webhook, copy its URL into Settings the same way, and tap Send test so Make can learn the data structure.
Events
JobProof sends a JSON POST to your URL. Failed deliveries are retried for about an hour.
gallery.ready
The job has a client link and every photo has uploaded. Sent once per link.
{
"id": "6d0e…",
"event": "gallery.ready",
"occurredAt": "2026-11-01T17:30:00.000Z",
"business": "Northside Interiors",
"job": {
"id": "b1f2…",
"title": "Sarah Miller, reception fit-out",
"address": "12 Oak Lane",
"externalId": "crm-77",
"shareUrl": "https://…/s/…",
"photoCount": 24,
"uploadedCount": 24,
"createdAt": 1793000000000,
"updatedAt": 1793000500000
}
}
job.completed
Sent when someone marks a job complete. The job object has the same fields as below, and every event's job also includes status (open or completed), dueAt, startedAt, completedAt, location, assignedTo and completedBy. Names are team member names.
job.created
A job was created on a phone. Same shape. Jobs created through the API don't send this event.
webhook.test
Sent by the Send test button. job is null.
Verifying signatures
Each request has an X-JobProof-Signature: t=<ms>,v1=<hex> header. v1 is the HMAC-SHA256 of t + "." + rawBody, keyed with the signing secret in Settings. Retries reuse the same event id, so use it to ignore duplicates.
API
Create a key in Settings and send it as Authorization: Bearer jp_…. Keys can read and create jobs and manage client links. They can't reach photos or your account.
Create a job
It appears on your phones at their next sync. With externalId, repeat calls return the existing job, so a retrying Zap won't make duplicates.
POST /api/v1/jobs
{ "title": "Lopez patio", "address": "9 Elm St", "notes": "Side gate", "externalId": "crm-77" }
201 { "job": { "id": "…", … }, "created": true }
200 { "job": { "id": "…", … }, "created": false }
List jobs
GET /api/v1/jobs?updatedSince=<ms>&limit=50
{ "jobs": [ … ] }
Get a job
GET /api/v1/jobs/:id
Turn a client link on or off
POST /api/v1/jobs/:id/share → { "shareUrl": "https://…/s/…" }
DELETE /api/v1/jobs/:id/share → { "shareUrl": null }
Turning a link off stops it working at once. Turning it on again makes a new link.