Skip to main content
A webhook is an address on your server to which Housera sends a POST every time something happens with one of your listings: it’s published, rejected, edited in the portal. You no longer need to ask periodically. Webhooks follow the open specification Standard Webhooks, so any compatible library will verify your signature.

Configuration

From your account, at Settings → API, in the Webhooks section:
1

Add address

A public https:// address, for example https://crm.exemplu.ro/hooks/housera. You choose the events you want to receive.
2

Keep secret

Upon creation, you receive the webhook secret, in the form whsec_…. It’s shown only once. You use it to verify the signature of every delivery.
3

Send a test

The “Send a test” button delivers an event webhook.test, signed like any other, solely to that webhook.
An account can have at most 5 webhooks. Each can be stopped and started from the same page. There, you also see the latest deliveries, with the status received from your server, and you can resend them manually. If the secret has reached where it shouldn’t, you rotate it: you receive a new one (shown only once), and the old one won’t sign anything anymore, immediately.

The events

State changes produce events regardless of where they originated: through API, from the portal, or from moderation. Events of a listing reach the account that owns the listing only if that account has at least one API key.

The format of an event

The body of each delivery is a JSON object:
string
The unique ID of the event. It’s the same as the header webhook-id. You use it so you don’t process the same event twice.
string
The type of the event, from the table above.
string
The moment of the event, ISO 8601.
object
For listing events: code, external_ref (or null), status (the state of the listing at the time of the event), url, plus fields specific to the type.
Upon deletion of the listing by you, by is owner and appears "deleted": true.
Your copy is now behind. Read the listing with GET /account/listings/{code} and decide what to keep. See For CRMs.
Possible reasons are in The Photos.

Delivery

Each delivery is a POST with the JSON body and headers:
  • Respond with a status 2xx within 10 seconds at most. Any other status, a redirect, or a timeout is a failed delivery.
  • Respond quickly, process later. Put the event in your own queue and confirm immediately; do not do all the synchronization while Housera waits.
  • Redirects are not followed. Configure the final address.

Retries

A failed delivery is retried automatically 6 more times, after 30 seconds, 2 minutes, 10 minutes, 30 minutes, 2 hours, and 6 hours. In total, Housera tries for about 9 hours. Events webhook.test are not retried. Because a delivery can arrive multiple times (for example, you processed it, but your response got lost) and because the order is not guaranteed:
  • remember the processed IDs (webhook-id) and ignore an already seen event;
  • do not rely on the order of arrival: for the current state of a listing, the source of truth remains GET /account/listings/{code}.

Signature verification

Any server can send a POST to your address. The signature proves that the delivery comes from Housera and has not been altered. The signature is an HMAC-SHA256 in base64, calculated over the string {webhook-id}.{webhook-timestamp}.{corp}, where {corp} are the exact bytes of the received body. The HMAC key is the part after whsec_ in the secret, decoded from base64. Steps:
  1. Read the raw body, before any JSON parsing. A re-serialized body will not yield the same signature.
  2. Check webhook-timestamp: reject deliveries older or newer than 5 minutes compared to your time. This way, an intercepted delivery cannot be resent later.
  3. Calculate the expected signature and compare it in constant time with each signature in webhook-signature (the header may contain multiple, separated by space, each prefixed with v1,).
Do not compare the signatures with ==. A regular comparison stops at the first differing character, and the timing difference can be used to guess the signature. Always use the constant-time comparison function of your language.

Event retrieval

A webhook that your server did not receive (was down for more than 9 hours, or the webhook was off) is not lost. Each event is kept for 35 days and you read it with GET /events (the right listings:read):
  • Events come in order, from the oldest to the newest, in the same form as in the webhook.
  • after is the ID of the last event you processed. Without after, you start from the oldest stored event.
  • limit ranges from 1 to 500 (default 100).
  • As long as meta.next is not null, there are more events: you request the next page with after equal to meta.next.
  • When meta.next is null, you have reached the end. Keep the ID of the last event from data and start from it next time.
A robust pattern: you process webhooks as they arrive and, every few minutes (or when restarting the server), run GET /events?after=<ultimul id> to catch anything missed. The IDs tell you what you’ve already processed.