Appearance
Webhooks
Register HTTPS endpoints in Settings → Integrations → Public API → Webhooks (or via API when webhooks:manage is granted).
Appraiser Flow sends JSON POSTs when subscribed events occur. Delivery is at-least-once — make handlers idempotent.
MVP events
| Event | When |
|---|---|
quote.created | Open Quote created (API, website, or other intake) |
quote.updated | Material public fields changed |
quote.sent | Quote sent to client |
quote.accepted | Quote accepted |
quote.expired | Quote expired |
Later waves add order.*, inspection.*, invoice.*, payment.*.
Headers
http
Content-Type: application/json
X-AF-Event: quote.created
X-AF-Delivery-Id: uuid
X-AF-Timestamp: 1724080000
X-AF-Signature: t=1724080000,v1=hexhmacVerify signature (required)
Compute HMAC-SHA256 over "{timestamp}.{raw_body}" using your endpoint signing secret. Compare to v1 using a constant-time equals.
Node.js
js
import crypto from "node:crypto";
function verify(sigHeader, rawBody, secret, maxSkewSec = 300) {
const parts = Object.fromEntries(
sigHeader.split(",").map((p) => p.trim().split("=")),
);
const t = parts.t;
const v1 = parts.v1;
if (!t || !v1) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > maxSkewSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}Python
python
import hmac
import hashlib
import time
def verify(sig_header: str, raw_body: bytes, secret: str, max_skew: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in sig_header.split(","))
t, v1 = parts.get("t"), parts.get("v1")
if not t or not v1:
return False
if abs(time.time() - int(t)) > max_skew:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)Respond 2xx quickly. Do heavy work async. Non-2xx triggers retries with backoff.
Payload shape (illustrative)
json
{
"id": "evt_…",
"type": "quote.created",
"created_at": "2026-08-19T18:00:00Z",
"data": {
"quote_id": "uuid",
"status": "open",
"external_reference": "WEB-10482"
}
}Security
- HTTPS only in staging/production
- Signing secret is shown once when you create or rotate the endpoint (store it like an API key)
- Rotate signing secrets if leaked
- Event
datais minimized (ids, status, external_reference) — not a full PII dump - Ignore events you do not understand (forward-compatible)
- Private/link-local webhook URLs are rejected; redirects are not followed
- Use Send test in Integrations after registering a URL
Authoritative controls: public-api-spec.md §5.5.