Channels

Outbound webhook

PropDrop sends a signed POST to your gateway for AI and team replies and key lead events, with X-PropDrop-Signature, an Idempotency-Key and retries.

The outbound webhook is for your own messaging gateway: n8n, a non-Meta WhatsApp provider, or an internal system. PropDrop POSTs signed JSON to your URL, and your gateway delivers the message to the customer or does whatever it needs with the event.

Do you need it?

If you connected WhatsApp, Messenger, Instagram or Telegram directly in PropDrop, you don't need it for replies: PropDrop sends them itself. Use it if you run your own gateway, or want another system to know when a lead is qualified, requests a viewing or is handed to your team. You'll usually pair it with the inbound webhook.

This page is for whoever sets up the gateway. Available on all plans.

Who can set it up: owner and managers.

Turn it on

Enter the Outbound URL

It must start with https:// and be publicly reachable. Save; you'll see "Outbound URL saved".

Generate the signing secret

Click Generate new secret. The secret (starting with pd_out_) is shown only once: copy it into your gateway and click I've copied it. Lost it? Generate a new one; the old one stops working immediately.

Switching it off clears the URL ("Outbound webhook turned off"), and any delivery still waiting for a retry is marked Failed.

When we send

Every request carries an event in the X-PropDrop-Event header:

EventSent when
message.createdThe AI agent or your team replies in a conversation that isn't tied to a channel connected in PropDrop (e.g. conversations that came in through the inbound webhook). Website chat replies are never sent, the customer already sees them
lead.qualifiedThe agent qualified the lead and scored it (hot / warm / cold)
viewing.requestedThe customer asked for a viewing (a request, not a confirmed slot: your team confirms)
handoffThe agent handed the conversation to your team

lead.qualified, viewing.requested and handoff are sent for every conversation, on any channel, as long as a URL is set.

The request

POST https://gateway.example.com/propdrop
Content-Type: application/json
User-Agent: PropDrop-Webhooks/1.0
X-PropDrop-Event: message.created
Idempotency-Key: 9b2e…
X-PropDrop-Signature: sha256=<hex HMAC-SHA256(secret, body)>

The body always uses the same envelope:

{
  "event": "message.created",
  "id": "9b2e…",
  "agency_id": "…",
  "created_at": "2026-10-09T12:00:00.000Z",
  "data": { }
}

id is the same value as the Idempotency-Key.

What's in data

An AI agent reply (sender = ai):

{
  "message_id": "…",
  "conversation_id": "…",
  "channel": "whatsapp",
  "sender": "ai",
  "text": "Hi Ahmed! We have 3-bedroom apartments in New Cairo starting from …",
  "contact": { "name": "Ahmed Ali", "phone": "+201001234567", "instagram": null }
}

A reply from your team (sender = agent):

{
  "message_id": "…",
  "conversation_id": "…",
  "channel": "whatsapp",
  "sender": "agent",
  "text": "…",
  "contact": { "name": "Ahmed Ali", "phone": "+201001234567", "instagram": null }
}

AI replies and team replies use the same shape; only sender differs (ai or agent). The customer is always in contact (name, phone, instagram).

Verify the signature

X-PropDrop-Signature = sha256= + the hex HMAC-SHA256 of the raw request body with your secret. Compute the same and reject anything that doesn't match:

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

function isFromPropDrop(rawBody, header, secret) {
  if (!header?.startsWith("sha256=")) return false;
  const expected = createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
  const got = header.slice(7);
  return got.length === expected.length && timingSafeEqual(Buffer.from(got, "hex"), Buffer.from(expected, "hex"));
}

Use the raw body

Sign-check the exact text you received, before any JSON.parse or re-serialising.

Responses and retries

  • Answer with any 2xx within 8 seconds and the delivery counts as Delivered.
  • No answer, a non-2xx status, or a redirect (3xx; we don't follow redirects) means a retry after about 1 minute, then about 5 minutes later: 3 attempts in total, after which the delivery is marked Failed.
  • Every retry sends exactly the same bytes, with the same signature and Idempotency-Key.

Idempotency-Key

The same event can reach you more than once (e.g. you answered late and we retried). Store the Idempotency-Key and ignore any request whose key you've already seen, so the same message is never sent to a customer twice. On our side, a given key is only ever queued once.

Recent deliveries

Below the settings you'll find the last 10 deliveries with their event, status (Delivered, Retrying, Failed) and attempt count. If you see failures, check your URL is up and answers 2xx quickly.

On this page