/api/partner/v1 · version 2026-08-26Installer API v1
For installation companies working jobs for companies on the platform. Pull a change feed of jobs, post status events, set schedules and assign technicians. Every write is idempotent and version-checked.
The job resource
Every response returns the installer-safe projection of a job: what a crew needs and nothing the company keeps to itself.
{
"id": "job_…", "version": 7, "status": "SCHEDULED",
"company": { "id": "org_…", "name": "Ridge Comfort Co." },
"customer": { "name": "Pat Example", "phone": "801-555-0100",
"address": { "line1": "123 Ridge Ave", "city": "Salt Lake City", "state": "UT", "zip": "84101" } },
"scope": { "packageName": "Heat pump 3-ton", "systemType": "HEAT_PUMP",
"equipment": [ { "sku": "…", "description": "…", "qty": 1 } ],
"accessories": [ … ], "supplierSources": [ … ], "scopeVersion": 3 },
"schedule": { "status": "CONFIRMED", "startsAt": "2026-09-18T14:00:00.000Z",
"endsAt": "2026-09-18T22:00:00.000Z", "timeZone": "America/Denver" },
"readiness": { "ready": true, "blockerCodes": [] },
"documents": [ { "id": "doc_…", "category": "SCOPE", "availableAt": "…" } ],
"technicianAssignments": [ { "membershipId": "mem_…", "assignedAt": "…" } ],
"updatedAt": "2026-09-13T15:02:11.000Z"
}
Absent on purpose: customer email, quote price, options, margin, financing, payments, signatures, and any other customer of the company. See the data boundary.
Headers on every request
| Header | Direction | Notes |
|---|---|---|
x-api-key | request | Installer-company key, <keyId>.<secret> |
Idempotency-Key | request (writes) | Required on POST and PUT. Up to 128 characters of A-Z a-z 0-9 . _ : -. Same key + same body replays the original response; same key + different body or operation → 409. |
x-ridge-arc-api-version | response | Currently 2026-08-26 |
x-request-id | response | Quote it in support requests |
Change feed
jobs:readReturns job changes for your company in sequence order. Persist nextCursor and pass it back to continue; without a cursor you get the latest page. limit 1 to 100.
{ "apiVersion": "2026-08-26",
"resources": [ { "sequence": 4182, "changedAt": "…", "resource": { …job… } } ],
"nextCursor": "eyJj…", "hasMore": false }
jobs:readOne job. 404 if it is not assigned to your company.
Status events
jobs:status:write| Field | Rules |
|---|---|
status | one of ACKNOWLEDGED EN_ROUTE ON_SITE BLOCKED IN_PROGRESS COMPLETE |
occurredAt | ISO-8601 UTC; at most 5 minutes in the future, 30 days in the past |
note | optional, up to 2,000 characters; visible to the company |
blockerCode | required when BLOCKED: CUSTOMER_UNAVAILABLE SITE_NOT_READY MATERIAL_MISSING EQUIPMENT_DAMAGED PERMIT WEATHER SAFETY OTHER |
expectedVersion | the job version you last read; 409 if the job changed |
Allowed transitions:
ASSIGNED → ACKNOWLEDGED | BLOCKED
ACKNOWLEDGED → EN_ROUTE | ON_SITE | BLOCKED
EN_ROUTE → ON_SITE | BLOCKED
ON_SITE → IN_PROGRESS | BLOCKED | COMPLETE
BLOCKED → ACKNOWLEDGED | EN_ROUTE | ON_SITE | IN_PROGRESS
IN_PROGRESS → BLOCKED | COMPLETE
COMPLETE, CANCELLED → (terminal; CANCELLED is set by the company)
POST /api/partner/v1/jobs/job_…/status-events
x-api-key: … Idempotency-Key: 3b1f7e2c-…
{ "status": "BLOCKED", "blockerCode": "SITE_NOT_READY", "occurredAt": "2026-09-18T15:05:00Z",
"note": "Electrical panel not upgraded yet", "expectedVersion": 7 }
201 { "apiVersion": "2026-08-26", "resource": { …job with version 8… } }
409 { "error": "Job changed; current version is 8" }
Schedule
jobs:schedule:write| Field | Rules |
|---|---|
status | TENTATIVE CONFIRMED CANCELLED |
startsAt, endsAt | ISO-8601 UTC or null; both required unless CANCELLED; end after start; a schedule that ended more than 30 days ago cannot be changed |
timeZone | IANA name (America/Denver); validated |
expectedVersion | job version |
A change made by either side emits install.schedule_changed to the other, so a company's dispatcher and your dispatch board converge.
Technicians and assignments
technicians:readYour company's technician memberships: id, display name, active flag. Use the membershipId in assignments.
jobs:assignment:writeBody { "membershipIds": [ … up to 20, unique … ], "expectedVersion": n }. Replaces the full assignment set; send an empty array to unassign everyone.
Documents
When install.document_available arrives, or a job lists a document, fetch it through the signed URL in the event or job payload. URLs are time-limited and bound to your company; do not store them, store the document id and re-request when needed.
Scope acknowledgements
If the company changes the scope after assignment you receive install.scope_acknowledgement_required with the new scopeVersion. Read the job, confirm your crew has the new scope, and post the next status event with the current version; the platform records the acknowledgement. Until then the job's readiness reports a SCOPE_ACK_REQUIRED blocker.
Completion
Post COMPLETE with photos and documents uploaded through the installer workspace or the document endpoints released to you. The company reviews and, once accepted, you receive install.completion_received with the evidence counts. Payment to installers is handled outside this API.
Webhooks for installers
The install.* family is listed on the webhooks page. A typical integration subscribes to install.job_assigned, install.schedule_changed, install.readiness_changed and install.scope_acknowledgement_required, and polls the change feed once a minute as a safety net.