Webhooks
Valara delivers real-time notifications to your HTTPS endpoint when events occur in your agency. 18 event types cover the full listing lifecycle, media delivery, scheduling, and invoicing.
Overview
When an event occurs, we POST a signed JSON payload to every subscribed endpoint. Your handler must:
- Return any
2xxstatus within 10 seconds (or we retry) - Verify the HMAC-SHA256 signature (or attackers can forge events)
- Deduplicate on
X-Event-Id(or retries and replays cause duplicate processing)
Configure endpoints and view delivery logs at
/manage/webhooks/ in the dashboard.
Request headers
Every delivery includes:
| Header | Value |
|---|---|
X-Webhook-Signature | sha256=<hex> - HMAC-SHA256 of "${timestamp}.${raw_body}" |
X-Webhook-Timestamp | Unix epoch seconds of delivery time |
X-Webhook-Event-Type | e.g. listing.created |
X-Event-Id | ULID - stable across retries and replays |
Content-Type | application/json |
Event payload envelope
{
"event_id": "01HXEVENTIDULIDHERE",
"event_type": "listing.created",
"api_version": "2026-05-29",
"timestamp": 1748476800,
"nonce": "01HXNONCEULIDHERE",
"is_sandbox": false,
"data": {
"listing_number": "ABC123",
"status": "Coming Soon",
"address": "123 Main St, Portland OR 97201"
}
}
event_id- stable ULID, matchesX-Event-Idheaderapi_version- locked data shape for this(event_type, api_version)pairis_sandbox-truefor events from sandbox (sk_test_*) activitydata- event-specific payload; shape locked perapi_version
Event types (18 total)
Listing events
| Event type | Trigger |
|---|---|
listing.created | New listing created via API or dashboard |
listing.status_changed | Status transitions (Coming Soon, Active, Sold, Off Market) |
listing.cancelled | Listing cancelled |
listing.section_updated | Matterport or floorplan section patched |
Media events
| Event type | Trigger |
|---|---|
media.delivery.completed | All ordered media delivered to the listing |
media.delivery.partial | Some media delivered; more pending |
media.delivery.failed | Delivery attempt failed (see data.reason) |
media.upload.completed | An API POST /media/upload request succeeded |
Scheduling events
| Event type | Trigger |
|---|---|
scheduling.appointment.created | Shoot appointment booked |
scheduling.appointment.rescheduled | Appointment rescheduled |
scheduling.appointment.cancelled | Appointment cancelled |
scheduling.appointment.completed | Shoot marked complete |
Invoice events
| Event type | Trigger |
|---|---|
invoice.created | New Square invoice created for a listing order |
invoice.paid | Invoice marked paid in Square |
invoice.overdue | Invoice past due date |
invoice.voided | Invoice voided |
User events
| Event type | Trigger |
|---|---|
user.agent_created | New agent account created in your hierarchy |
Webhook events
| Event type | Trigger |
|---|---|
webhook.test_fire | Test fire from dashboard verifier or endpoint test button |
Signature verification
Every delivery is signed with HMAC-SHA256. Verify before processing:
// Node.js example
import crypto from "crypto";
function verifySignature(signingSecret, timestamp, rawBody, signature) {
// 1. Check replay window (5 minutes)
const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - parseInt(timestamp, 10));
if (ageSeconds > 300) return false;
// 2. Compute expected signature
const expected = crypto
.createHmac("sha256", signingSecret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
// 3. Constant-time comparison
const sigBuf = Buffer.from(signature, "hex");
const expBuf = Buffer.from(expected, "hex");
return sigBuf.length === expBuf.length && crypto.timingSafeEqual(sigBuf, expBuf);
}
See SDKs and examples for full handler code in Node.js, Python, Ruby, and PHP.
Retry schedule
On non-2xx response or connection timeout, we retry:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 30 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the delivery is marked failed. Your
endpoint's consecutive_failures counter increments. At 50 consecutive
failures the endpoint is auto-disabled with reason
auto_disabled_consecutive_failures. You receive an email notification
and the health dashboard shows the disabled state.
To re-enable: fix your handler, toggle the endpoint back on in
/manage/webhooks/endpoints.
Replaying deliveries
Any past delivery (successful or failed) can be replayed from
/manage/webhooks/logs by clicking Replay on a log row.
Replay behavior:
- Uses the original payload and headers exactly
- Reuses the original
X-Event-Id(tests your idempotency logic) - Signs with the current signing secret
- Skips initial backoff - delivery starts immediately
- Rate limit: 10 replays per minute per endpoint
Built-in HMAC verifier
/manage/webhooks/verifier lets you send a real signed test fire to
your endpoint and inspect the result without writing any code:
- Select your endpoint
- Select an event type
- Click Send test fire
- View verdict: Verified / Signature rejected / Handler error / Connection failed / Timeout
The verifier shows your endpoint's HTTP response and a diagnostic hint on non-verified results.
Configuring endpoints
Create and manage endpoints at /manage/webhooks/endpoints.
Required fields:
- Name - descriptive label (e.g. "Production CRM handler")
- URL - HTTPS URL (HTTP is rejected)
- Event types - which events to subscribe to
- Environment - Live events only / Sandbox events only / Both
The signing secret is shown once on creation via the reveal-once portal. Store it in your secrets manager.
Rate limits
| Operation | Limit |
|---|---|
| Inbound deliveries per endpoint | 1000 events/hour |
| Replay | 10/min per endpoint |
| Test fire | 5/min per endpoint |
If your handler is slower than delivery arrives, queue events on your
side and return 2xx immediately. Do not hold the connection open.