Webhooks.
Beamprobe sends a signed HTTPS POST to your endpoint when something happens in your data rooms: a link is opened, a document is read or downloaded, an NDA is accepted, a signature request is signed or declined. Point it at your CRM, Zapier, Make, Slack automation or your own code.
Business plan · set up in Settings, Webhooks · one URL can subscribe to any set of events
Set up
- In the app, open Settings, Webhooks.
- Enter an https:// URL on a public address and pick the events. Private, local and internal addresses are refused.
- Copy the signing secret. It is shown once; rotate it any time from the same page.
- Press "Send test event" to receive a ping and check your handler.
Events
room.viewed
A recipient opens a link to one of your rooms (bot-filtered).
data: room_id, room_name, link_id, link_name, viewer_email, viewer_ip, viewed_at
document.viewed
A recipient opens a document, once per document per viewing session.
data: room_id, room_name, link_id, document_id, document_name, viewer_email, viewed_at
document.downloaded
A recipient downloads a document from a link with downloads on.
data: room_id, room_name, link_id, document_id, document_name, viewer_email, downloaded_at
nda.accepted
A recipient accepts the NDA on a gated link.
data: room_id, room_name, link_id, signer_email, signer_ip, accepted_at
room.created
You or your team create a data room (not the hidden rooms behind Quick Share links).
data: room_id, room_name, created_at
signature.signed
A signer signs one of your signature requests.
data: request_id, request_title, room_id, signer_email, signer_name, at
signature.declined
A signer declines one of your signature requests.
data: request_id, request_title, room_id, signer_email, signer_name, decline_reason, at
Times are ISO 8601 in UTC. viewer_email is present when the link has an email gate or NDA, and null otherwise.
Request
Every delivery is a JSON POST with these headers and an envelope around the event data.
POST /your/endpoint HTTP/1.1
content-type: application/json
user-agent: Beamprobe-Webhook/1.0
x-beamprobe-event: nda.accepted
x-beamprobe-event-id: evt_Qm9vX2V4YW1wbGVfaWQ
x-beamprobe-timestamp: 1793011200
x-beamprobe-signature: t=1793011200,v1=5b1f0c...e9a2
{
"id": "evt_Qm9vX2V4YW1wbGVfaWQ",
"event": "nda.accepted",
"created": 1793011200,
"data": {
"room_id": 42,
"room_name": "Project Lighthouse",
"link_id": 318,
"signer_email": "[email protected]",
"signer_ip": "203.0.113.24",
"accepted_at": "2026-10-22T09:41:07Z"
}
}
Verify the signature
The signature is HMAC-SHA256(secret, timestamp + "." + raw_body) in hex, sent as v1 in x-beamprobe-signature. Compute it over the raw body before parsing, compare in constant time, and reject timestamps more than five minutes old.
Node.js
import crypto from "node:crypto"
// Use the raw request body, before any JSON parsing.
export function verifyBeamprobe(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")))
const timestamp = Number(parts.t)
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return false
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex")
const a = Buffer.from(expected)
const b = Buffer.from(parts.v1 || "")
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
Python
import hashlib, hmac, time
def verify_beamprobe(raw_body: bytes, signature_header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in signature_header.split(","))
timestamp = int(parts.get("t", "0"))
if abs(time.time() - timestamp) > 300:
return False
expected = hmac.new(
secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Retries and duplicates
- Answer with any 2xx status within 10 seconds. Redirects are not followed.
- A failed delivery is retried twice, after 10 seconds and after a minute. Retries carry the same x-beamprobe-event-id, so store it and ignore repeats.
- The settings page shows the last success, the last failure and its status code for each endpoint.
- Deliveries are not guaranteed to arrive in order. Use the timestamps in the data.
Data and privacy
Payloads can include a recipient's email and IP address. You choose where they go, so treat your endpoint as part of your own processing under your privacy notice. Delete an endpoint and deliveries stop at once.