fb-pixel Outbound webhooks: stream contact events to your own systems | Help Center - Revamp365.ai
Skip to main content
Revamp365.ai

Integrations & Apps

Outbound webhooks: stream contact events to your own systems

5 min read · 7 views

What outbound webhooks do

An outbound webhook endpoint is a URL you control that Revamp365 will HTTPS POST a JSON event to whenever something interesting happens in your CRM. Use this to mirror contact changes into your data warehouse, trigger a Slack alert, fire a Zap, or update an external system.

Time to set up: ~5 minutes. Result: events streamed to your endpoint with HMAC-signed payloads, automatic retries, and full delivery logs.

Outbound webhooks are different from inbound lead webhooks (which receive leads into Revamp). See Send leads into Revamp365 for the inbound flow.

Events you can subscribe to

Each endpoint has its own event allowlist. The current set:

EventFires when
contact_createdA new Contact is added to the CRM (any source).
lead_status_changedLead status moves (e.g. NEW → QUALIFIED → WON).
lead_temperature_changedTemperature changes (Cold / Cool / Warm / Hot).
tag_addedA tag is added to a Contact.
tag_removedA tag is removed.
note_addedA timeline note is added to a Contact.
ai_summary_updatedThe AI summary on a Contact is regenerated.

One user action can fire multiple events. Adding a tag fires tag_added, then ~30 seconds later ai_summary_updated when the summary regenerates. Build your receiver to be idempotent.

Step 1 — Create an endpoint

  1. Open Integrations & Apps → Webhooks → Endpoints.
  2. Click Add endpoint.
  3. Enter your receiving URL. Must be https:// and resolve to a public host (not a private IP).
  4. Optionally enter a label ("Slack alerts", "Snowflake mirror", etc.).
  5. Tick the events you want pushed. Select All sends everything.
  6. Click Save.
A signing secret is shown once on creation. Copy it before closing the dialog. You need it to verify the HMAC on every incoming POST.

Step 2 — What the POST looks like

Every event arrives as a JSON document with a stable shape:

POST /your-endpoint HTTP/1.1
Host: your.app
Content-Type: application/json
X-Webhook-Signature: sha256=<hex_hmac_of_request_body>
X-Webhook-Event: tag_added
X-Webhook-Delivery: 01HZX…<ulid>

{
  "event": "tag_added",
  "delivery_id": "01HZX…",
  "occurred_at": "2026-04-27T18:14:22Z",
  "contact": {
    "id": 12345,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "[email protected]",
    "phone": "+15125550100",
    "lead_status": "QUALIFIED",
    "tags": ["seller", "motivated", "hot-lead"]
  },
  "change": {
    "tag": "hot-lead"
  }
}

Always trust contact.id for matching, not name/email.

Step 3 — Verify the HMAC signature

Before processing the body, verify the X-Webhook-Signature header. Reject any request whose signature doesn't match.

The signature is hmac_sha256(raw_body, signing_secret), hex-encoded, prefixed with sha256=.

Node.js (Express)

const crypto = require('crypto');

app.post('/revamp-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = (req.get('X-Webhook-Signature') || '').replace(/^sha256=/, '');
  const expected = crypto
    .createHmac('sha256', process.env.REVAMP_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');
  if (sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body.toString('utf8'));
  // … process …
  res.sendStatus(202);
});

PHP (Laravel)

$raw = $request->getContent();
$sig = str_replace('sha256=', '', $request->header('X-Webhook-Signature', ''));
$expected = hash_hmac('sha256', $raw, config('services.revamp.webhook_secret'));
if (! hash_equals($expected, $sig)) {
    abort(401);
}
$event = json_decode($raw, true);

Retries + status

Your endpoint should respond with a 2xx status as quickly as possible. Anything else (timeouts > 15s, 4xx, 5xx) is treated as a failure.

Log statusMeaning
successEndpoint returned 2xx within 15s.
pending_retryFailed once. Will retry with exponential backoff.
failedAll retries exhausted. Manual Retry button is available.

Open Webhooks → Delivery Logs to inspect every POST: full request body, headers, response status, response time, and the retry count.

Limits + safety

LimitDefault
Endpoints per user10
Receiver timeout15 seconds
Max payload size1 MB
Private / loopback URLsBlocked (SSRF guard)

Endpoints to private IPs (10.x, 192.168.x, 169.254.x, etc.) are rejected on save. Use a public ingress or a tunnel service for local development.

Troubleshooting

What you seeLikely causeFix
Endpoint won't save — "URL not allowed"SSRF guard rejecting private host.Use a public domain. Tunnels (ngrok, Cloudflare) work.
All deliveries failed with no response bodyReceiver timing out (> 15s).Acknowledge fast (queue work + return 202 immediately).
Receiver gets 401 from your own checkReading parsed JSON instead of raw body for HMAC.HMAC over raw bytes — no JSON re-serialization.
Same contact triggers 3 events at onceWorking as designed — one user action ⇒ multiple events.De-dupe in your receiver using delivery_id.
Endpoint disappearedDeleted by an admin. Logs are kept.Recreate the endpoint; logs remain searchable.

FAQ

Is the order of events guaranteed?
No. Process events idempotently. occurred_at tells you when the source change happened.
Can I rotate the signing secret?
Yes — edit the endpoint, click Rotate secret. Old secret stops working immediately, so update your verifier in the same window.
What's the difference between this and CRM Sync?
CRM Sync is a managed integration with Podio/Salesforce that does field mapping. Outbound webhooks are raw event streams to any URL — bring your own translation layer.
Do deletes fire an event?
Not currently. Add it to your wishlist with support if you need it.
Are deletes of an endpoint reversible?
Endpoints are hard-deleted, but Delivery Logs for past traffic are retained. Recreate the endpoint with the same URL to resume.

Was this article helpful?