APIs

Webhooks

Signed, retried, de-duplicable HTTPS deliveries whenever something your integration cares about changes. Verify first, parse second.

Subscriptions

A company owner creates subscriptions in the console (POST /partner-api/webhooks) with your endpoint URL and the events you want. Rules enforced at creation and re-checked on every delivery:

Envelope

Every delivery is a JSON object with the same top-level shape regardless of event:

{
  "id": "evt_01J…",                // event id; stable across retries
  "event": "install.status_changed",
  "apiVersion": "2026-08-26",
  "schemaVersion": 1,
  "occurredAt": "2026-09-13T17:42:10.512Z",
  "resource": { "type": "installJob", "id": "job_…", "version": 7 },
  "data": { … }                    // event-specific payload, see catalogue
}

The resource.version is the same optimistic-concurrency version the Installer API uses; if you write back, send it as expectedVersion.

Headers

HeaderMeaning
x-ridge-arc-eventEvent name, duplicated from the body for routing before parsing.
x-ridge-arc-delivery-idUnique per delivery attempt. De-duplicate on this if you want at-most-once processing.
x-ridge-arc-timestampUnix seconds when we signed the payload. Reject if more than 300 seconds from your clock.
x-ridge-arc-secret-versionWhich secret version signed this delivery. Needed during rotation.
x-ridge-arc-signaturev1=<hex>: HMAC-SHA256 over timestamp + "." + rawBody using the secret of that version.
content-typeapplication/json; the raw bytes are what is signed, so do not re-serialise before verifying.

Verifying a signature

Compute the HMAC over the exact raw request body, compare in constant time, then check the timestamp window, then de-duplicate. Only then parse JSON.

// Node 18+ (Express with raw body)
import crypto from "node:crypto";

export function verifyRidgeArc(req, secretsByVersion) {
  const ts = req.header("x-ridge-arc-timestamp");
  const version = req.header("x-ridge-arc-secret-version");
  const given = req.header("x-ridge-arc-signature") || "";
  const secret = secretsByVersion[version];
  if (!secret || !ts) return false;
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;   // replay window
  const expected = "v1=" + crypto.createHmac("sha256", secret)
    .update(ts + "." + req.rawBody.toString("utf8")).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(given);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
# Python 3 (Flask / FastAPI raw body)
import hmac, hashlib, time

def verify_ridge_arc(headers, raw_body: bytes, secrets_by_version: dict) -> bool:
    ts = headers.get("x-ridge-arc-timestamp", "")
    secret = secrets_by_version.get(headers.get("x-ridge-arc-secret-version", ""))
    if not secret or not ts or abs(time.time() - float(ts)) > 300:
        return False
    expected = "v1=" + hmac.new(secret.encode(), (ts + ".").encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, headers.get("x-ridge-arc-signature", ""))

Known-good sample for an offline test: secret whsec_test_0123456789abcdef, timestamp 1757778130, body {"id":"evt_test","event":"install.test","apiVersion":"2026-08-26","schemaVersion":1,"occurredAt":"2026-09-13T15:42:10Z","resource":{"type":"subscription","id":"sub_test","version":1},"data":{}}. Your verifier should accept the signature the sandbox sends for that delivery and reject it when any byte of the body changes.

Acknowledging and retries

Secret rotation (two phases)

  1. Rotate (POST /partner-api/webhooks/:id/rotate-secret): a pending secret with the next version number is created and shown once. Deliveries are still signed with the current version.
  2. You load the pending secret into your verifier keyed by its version.
  3. Activate (POST /partner-api/webhooks/:id/activate-secret with {"pendingSecretVersion": n}): new deliveries are signed with version n. Keep the previous version available for a short overlap so in-flight retries still verify, then drop it.

Event catalogue

The live list is at GET /partner-api/webhook-events (owner session), which also reports the signature scheme version. Events are grouped by the subscription that can receive them; your partner type limits which groups you can subscribe to (see Access & compliance).

Sales and project events

EventWhenTypical consumer
quote.sentA quote was sent to the customerLead source attribution, CRM stage sync
quote.viewedThe customer opened the quote linkCRM engagement
quote.acceptedThe customer accepted an optionCRM, financing, accounting
project.stage_changedAny pipeline stage changeCRM, reporting
appointment.bookedAn appointment was created or movedBooking vendors, calendars
deposit.paid / deposit.refundedCustomer deposit settled or reversedAccounting, financing
invoice.issuedAn invoice was issuedAccounting
rebate.status_changedA rebate or incentive application changed stateIncentive programs
project.completedProject closed outCRM, accounting, review requests

Installation events (installer companies)

EventWhen
install.job_assigned / install.job_unassignedA job was assigned to or removed from your company
install.job_cancelledThe job was cancelled by the company
install.schedule_changedScheduled start or end changed (by either side)
install.readiness_changedReady-to-install flag or blocker codes changed (permits, equipment, deposit)
install.document_availableA scope, permit, manual, warranty or completion document is available to fetch
install.status_changedJob status moved
install.completion_receivedYour completion report was accepted
install.scope_acknowledgement_requiredThe scope changed; your acknowledgement of the new scope version is needed before work continues
install.testOn-demand test delivery; safe to ignore in production

Financial journal events (accounting partners)

Journal-level events for deposits, invoices, payouts and adjustments are listed in the live catalogue and issued only to subscriptions on an accounting-class partner. They never carry customer contact details.

Idempotent consumers

Store id (event) and x-ridge-arc-delivery-id. Process an event once even if several deliveries of it arrive; acknowledge every delivery of an already-processed event with 2xx so retries stop.