Docs · Webhooks

Webhooks

Room Profit emits webhooks for every significant event across the modules deployed on your account — a new rate suggestion, a drafted review reply, a WhatsApp reply from a guest, or an issued VAT invoice. Subscribe once and we deliver over HTTPS.

Subscribing

Create a subscription with a POST to /webhooks. You provide a target URL (must be HTTPS), an array of event types, and an optional description. We return a subscription ID and a signing secret. The signing secret is shown once — store it before the response window closes.

curl -X POST https://api.profit.dapokl.com/v1/webhooks \
  -H "Authorization: Bearer rp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://ops.hotel-marszalkowska.pl/webhooks/roomprofit",
    "events": ["rate.suggested", "invoice.issued"],
    "description": "Ops pipeline v2"
  }'

Event types

EventDescription
rate.suggestedAutopricer has a new rate suggestion pending approval.
rate.approvedA rate suggestion was approved and pushed to Profitroom Booking Engine.
rate.rejectedA rate suggestion was rejected.
review.draftedReviews Aggregator has drafted a reply to a new review.
review.publishedA review reply was published to the source platform.
whatsapp.receivedA guest replied to a WhatsApp Concierge message.
whatsapp.sentA WhatsApp Concierge template was successfully delivered.
invoice.issuedA KSeF invoice was issued for a completed reservation.
invoice.correctedA correcting invoice (faktura korygująca) was issued.
connection.d_edge.failedA Profitroom API call failed authentication or hit an unexpected error.

Payload shape

Every payload is JSON. The top-level envelope is stable across event types:

{
  "id": "evt_2b7d1c8a19f4",
  "type": "rate.suggested",
  "created_at": "2026-07-28T09:12:44Z",
  "delivery_id": "del_a92c81",
  "property_id": "prop_1489",
  "data": {
    "suggestion_id": "rsg_87542",
    "room_type_code": "STDD",
    "date": "2026-08-14",
    "current_rate": { "currency": "EUR", "minor_units": 12000 },
    "proposed_rate": { "currency": "EUR", "minor_units": 13400 },
    "trigger": "pace_ahead_15pct"
  }
}

HMAC signature

We sign every delivery with HMAC-SHA256 over the raw request body, using the subscription's signing secret. The signature is sent in X-Roomprofit-Signature as a hex string. Verify by recomputing HMAC on the raw body — not the parsed JSON — and comparing constant-time.

// Node example
const crypto = require('crypto');
const expected = crypto
  .createHmac('sha256', process.env.RP_WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sigHeader))) {
  return res.status(401).end();
}

Delivery guarantees

At-least-once. We consider a delivery successful when your endpoint returns HTTP 2xx within 15 seconds. Anything else — timeout, non-2xx, connection refused — is a failure and we retry.

Retry policy

Failed deliveries retry with exponential backoff over 72 hours: at 1 min, 5 min, 15 min, 1h, 4h, 12h, 24h, 48h, 72h. After 72 hours we mark the event dead-lettered. You can inspect and manually retry dead-lettered events in the Room Profit dashboard, or via POST /webhooks/deliveries/{id}/retry up to 30 days after the original event.

Rate limits on your endpoint

We will not deliver more than 20 events per second per subscription; excess is queued. If you need higher throughput, split events across multiple subscriptions with different filter sets.

Testing

Every subscription supports a POST /webhooks/{id}/ping that sends a synthetic webhook.ping event. Use this to verify signing before wiring up production events.