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, …)
- Create a Zap with the trigger Webhooks by Zapier → Catch Hook; copy the generated URL.
- Set it as your
webhook_url(step 2 above), then trigger a test or wait for a real event so Zapier learns the shape. - Add a Filter step on
severity = high(or specifickindvalues likedeed_recorded) so your CRM only sees what matters. - Map fields into your CRM action — e.g. Salesforce "Create Task" or HubSpot "Create Engagement" with
property.addressandsummary. 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.