APIs · base /api/partner/v1 · version 2026-08-26

Installer 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

HeaderDirectionNotes
x-api-keyrequestInstaller-company key, <keyId>.<secret>
Idempotency-Keyrequest (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-versionresponseCurrently 2026-08-26
x-request-idresponseQuote it in support requests

Change feed

GET/partner/v1/jobs?cursor=&limit=50scope jobs:read

Returns 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 }
GET/partner/v1/jobs/:idscope jobs:read

One job. 404 if it is not assigned to your company.

Status events

POST/partner/v1/jobs/:id/status-eventsscope jobs:status:write
FieldRules
statusone of ACKNOWLEDGED EN_ROUTE ON_SITE BLOCKED IN_PROGRESS COMPLETE
occurredAtISO-8601 UTC; at most 5 minutes in the future, 30 days in the past
noteoptional, up to 2,000 characters; visible to the company
blockerCoderequired when BLOCKED: CUSTOMER_UNAVAILABLE SITE_NOT_READY MATERIAL_MISSING EQUIPMENT_DAMAGED PERMIT WEATHER SAFETY OTHER
expectedVersionthe 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

PUT/partner/v1/jobs/:id/schedulescope jobs:schedule:write
FieldRules
statusTENTATIVE CONFIRMED CANCELLED
startsAt, endsAtISO-8601 UTC or null; both required unless CANCELLED; end after start; a schedule that ended more than 30 days ago cannot be changed
timeZoneIANA name (America/Denver); validated
expectedVersionjob 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

GET/partner/v1/techniciansscope technicians:read

Your company's technician memberships: id, display name, active flag. Use the membershipId in assignments.

PUT/partner/v1/jobs/:id/assignmentsscope jobs:assignment:write

Body { "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.