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, "workOrderNo": "WO-1042", "status": "ON_SITE",
  "customer": { "name": "Pat Example", "phone": "801-555-0100",
    "serviceAddress": { "line1": "123 Ridge Ave", "city": "Salt Lake City", "state": "UT", "postalCode": "84101" } },
  "scope": { "packageName": "Heat pump 3-ton", "systemType": "HEAT_PUMP",
    "equipment": { "condenser": { "make": "Example", "model": "HP-36", "tonnage": 3 } },
    "accessories": ["Smart thermostat"], "supplierSources": [] },
  "schedule": { "status": "CONFIRMED", "version": 2,
    "startsAt": "2026-09-18T14:00:00.000Z", "endsAt": "2026-09-18T22:00:00.000Z",
    "timeZone": "America/Denver" },
  "technicianAssignments": [ { "membershipId": "mem_…", "assignedAt": "2026-09-17T15:00:00.000Z" } ],
  "updatedAt": "2026-09-18T15: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 the feed starts after sequence zero. limit 1 to 100.

{ "apiVersion": "2026-08-26",
  "resources": [ { "sequence": 4182, "changedVersion": 7, "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 }

200 { "ok": true, "jobId": "job_…", "status": "BLOCKED", "version": 4,
        "resourceVersion": 8, "occurredAt": "2026-09-18T15:05:00.000Z", "blockerCode": "SITE_NOT_READY" }
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

The install.document_available event carries document metadata. Agree the authorized retrieval mechanism during onboarding; a document event does not itself contain a download URL. The job resource above does not include a document list.

Scope acknowledgements

The install.scope_acknowledgement_required event identifies a changed scope and its version. Use the acknowledgement workflow authorized for your integration; a status-event write is not a scope acknowledgement.

Completion

A COMPLETE status event records installation progress. Evidence submission, quality review and financing approval are separate operations. The published Installer API has no photo-upload endpoint; agree the evidence interface and required fields with your partner. See the financing and QC integration design.

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.