Skip to main content

Enterprise

CRM & automation integrations

PearlAudit doesn't ship native CRM plugins — it ships something more durable: a documented REST API and HMAC-signed webhooks for property-monitoring events. Any system that can receive an HTTPS request — Zapier, Make, HubSpot workflows, or fifty lines of your own code — can consume deed recordings, new violations, façade-status flips, and the rest of the 17-signal watch feed. Events reflect when NYC publishes a record (at least weekly for enforcement feeds), not the moment it happens.

1. Get a Team or Enterprise API key

Monitoring over the API needs a Team+ key, ideally scoped to watch (least privilege — it can manage the watchlist and nothing else). Mint keys at /account/api-keys; full reference at pearlaudit.com/v1/docs.

2. Watch properties and set your webhook

Add properties one at a time, or watch an entire screened portfolio in one call with POST /v1/watchlist/from-bulk. Then register your endpoint — the signing secret is returned once, so store it then.

# 1. Watch a property (or POST /v1/watchlist/from-bulk with a bulk job_id)
curl -X POST https://pearlaudit.com/v1/watchlist \
  -H "Authorization: Bearer ta_YOUR_KEY" \
  -H "content-type: application/json" \
  -d '{"bbl": "1008350041", "label": "350 Fifth Ave"}'

# 2. Point events at your endpoint — the response includes webhook_secret ONCE
curl -X PUT https://pearlaudit.com/v1/watchlist/prefs \
  -H "Authorization: Bearer ta_YOUR_KEY" \
  -H "content-type: application/json" \
  -d '{"webhook_url": "https://hooks.your-crm.example/pearlaudit"}'

First sight is a silent baseline: you get events for what changes after you start watching, never a dump of history.

3. Receive watch.event deliveries

Every event POSTs JSON like this, with retries and a per-delivery x-pearlaudit-event-id you can dedupe on:

{
  "event": "watch.event",
  "event_id": "evt_9f2c41d0a3b85e17c6d20b44",
  "property": {
    "bbl": "MN008350041",
    "label": "350 Fifth Ave",
    "address": "350 5th Ave, Manhattan"
  },
  "signal": "acris_recent",
  "kind": "deed_recorded",
  "severity": "high",
  "summary": "A deed (DEED) was recorded against this property for $52,000,000, dated 2026-05-28 (ACRIS document 2026060400121001). NYC ACRIS recorded documents, as of 2026-06-15. Publication lags recording — this reflects when NYC published the record, not when it happened.",
  "detail": { "document_id": "2026060400121001", "doc_type": "DEED", "doc_date": "2026-05-28", "amount_usd": 52000000 },
  "detected_at": "2026-07-11T03:05:12+00:00"
}

severity: "high" marks the events most integrations route to a human (recorded deeds and mortgages, façade status becoming UNSAFE, AEP designation, priority-hazard complaints). Failed deliveries are retried and ledgered — nothing silently disappears.

4. Verify the signature

Two schemes ride every delivery: v1 (byte-stable HMAC of the raw body) and v2 (timestamp-bound, rejects replays). Verify at least one; v2 if you can.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyPearlAudit(req: { headers: Record<string, string>; rawBody: Buffer }, secret: string): boolean {
  // v1 — byte-stable: sha256=HMAC-SHA256(secret, raw_body)
  const v1 = "sha256=" + createHmac("sha256", secret).update(req.rawBody).digest("hex");
  const sigOk = timingSafeEqual(Buffer.from(v1), Buffer.from(req.headers["x-pearlaudit-signature"] ?? ""));

  // v2 — replay-guarded: HMAC-SHA256(secret, "<timestamp>.<raw_body>")
  const ts = req.headers["x-pearlaudit-timestamp"] ?? "";
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300; // reject > 5 min drift
  const v2 = createHmac("sha256", secret).update(ts + "." + req.rawBody).digest("hex");
  const v2Ok = timingSafeEqual(Buffer.from(v2), Buffer.from(req.headers["x-pearlaudit-signature-v2"] ?? ""));

  return sigOk || (fresh && v2Ok);
}
import hashlib, hmac, time

def verify_pearlaudit(headers: dict, raw_body: bytes, secret: str) -> bool:
    # v1 — byte-stable
    v1 = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    if hmac.compare_digest(v1, headers.get("x-pearlaudit-signature", "")):
        return True
    # v2 — replay-guarded
    ts = headers.get("x-pearlaudit-timestamp", "")
    if not ts or abs(time.time() - float(ts)) > 300:
        return False
    v2 = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(v2, headers.get("x-pearlaudit-signature-v2", ""))

Recipes

Zapier → any CRM (Salesforce, HubSpot, Pipedrive, …)

  1. Create a Zap with the trigger Webhooks by Zapier → Catch Hook; copy the generated URL.
  2. Set it as your webhook_url (step 2 above), then trigger a test or wait for a real event so Zapier learns the shape.
  3. Add a Filter step on severity = high (or specific kind values like deed_recorded) so your CRM only sees what matters.
  4. Map fields into your CRM action — e.g. Salesforce "Create Task" or HubSpot "Create Engagement" with property.address and summary. The summary is a complete, citation-bearing sentence — safe to show a broker as-is.

Make (Integromat)

Add a Custom webhook module, use its URL as your webhook_url, and route into any downstream module. Make's router + filters map cleanly onto signal / kind / severity.

Roll your own receiver

An HTTPS endpoint + the verification snippet above is the whole job. Respond 2xx quickly (we retry non-2xx), dedupe on x-pearlaudit-event-id, and enrich on your side by calling GET /v1/query?bbl=… with the same key when you want the full property object behind an event.

No engineering bandwidth?

The same events are available as email alerts (immediate for high-severity, weekly or monthly digests otherwise) and in the in-app feed at /account/watchlist — no integration required.

Native marketplace connectors (Zapier app, HubSpot app) are on the roadmap and will be prioritized by customer demand — if one would unblock your team, tell us which.