Audit Export
Stream audit events to your own systems. HMAC-signed webhooks for any endpoint, plus native formats for Splunk HEC, Datadog, and Elastic.
Audit export pushes PgBeam events to your systems as they happen. Point it at a webhook endpoint and PgBeam delivers a signed JSON payload for each event. Point it at a SIEM and PgBeam formats the events the way that SIEM expects. The audit log keeps the full history for querying; export is for getting events out in real time.
Event types
| Event | Fires when |
|---|---|
query_blocked | A statement is rejected by policy (allowlist, read-only, etc.). |
budget_exhausted | A credential hits its query or row budget. |
kill_switch | A credential or project kill-switch is tripped. |
masked | A result is returned with one or more masked columns. |
migration_flagged | A DDL statement is flagged by the safe-migration linter. |
approval_requested | A write or DDL is held for approval. |
anomaly_alert | Anomaly detection raises an alert. |
For each event's full payload shape, see webhook events.
Create a webhook endpoint
pgbeam webhooks create https://hooks.example.com/pgbeam \
--event query_blocked,kill_switch,anomaly_alertOpen Webhooks, add an endpoint URL, pick the events to send, and copy the signing secret. Use Send test event to verify your receiver before you rely on it.
{
"url": "https://hooks.example.com/pgbeam",
"event_types": ["query_blocked", "kill_switch", "anomaly_alert"],
"format": "json"
}You set the signing secret when you create the endpoint. It is write-only: PgBeam stores it to sign deliveries and never returns it, so keep your own copy. The same secret verifies every delivery.
Webhook payload
Each delivery is a JSON body with the event, the project, and the event-specific
detail under data. The webhook events page documents the
data fields for every event type.
{
"id": "whd_2a9f1c",
"type": "query_blocked",
"project_id": "prj_abc",
"occurred_at": "2026-06-13T09:24:11.512Z",
"data": {
"audit_id": "aud_9f2c",
"credential_id": "agent_4f2c",
"region": "us-east-1",
"event": "blocked",
"sql": "DELETE FROM users",
"reason": "policy is read-only: DELETE is not allowed"
}
}PgBeam signs every native (json) and elastic delivery. Two signature headers
are sent, both as sha256=<hex> keyed with your signing secret:
X-PgBeam-Signature(v1) is the HMAC-SHA-256 of the raw request body only.X-PgBeam-Signature-V2(v2) is the HMAC-SHA-256 of the exact byte stringtimestamp + "." + body, wheretimestampis the same value sent in theX-PgBeam-Timestampheader (unix seconds, as a decimal string) and.is a single literal period. Because the timestamp is part of the signed bytes, a captured delivery cannot be replayed with a rewritten timestamp: rewriting it invalidates the signature. This is the same construction Stripe uses.
Each delivery also carries X-PgBeam-Event, X-PgBeam-Event-Id,
X-PgBeam-Timestamp (unix seconds), and X-PgBeam-Delivery (a per-attempt id
for de-duplication).
We recommend verifying X-PgBeam-Signature-V2 and rejecting stale timestamps.
v1 stays in place unchanged for existing receivers, so you can migrate at your
own pace. The SIEM/token destinations (Splunk HEC, Datadog) carry neither
signature; they authenticate with the destination's own token.
import { createHmac, timingSafeEqual } from "node:crypto";
// Signed bytes are exactly: timestamp + "." + rawBody
export function verifyV2(
rawBody: string,
timestamp: string, // the X-PgBeam-Timestamp header, verbatim
signatureHeader: string, // the X-PgBeam-Signature-V2 header
secret: string,
toleranceSeconds = 300,
): boolean {
// Reject stale timestamps first to enforce a replay window.
const ts = Number(timestamp);
if (!Number.isFinite(ts)) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - ts) > toleranceSeconds) return false;
const expected =
"sha256=" +
createHmac("sha256", secret)
.update(timestamp + "." + rawBody)
.digest("hex");
const a = Buffer.from(signatureHeader);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}Verify against the raw request body, before any JSON parsing reserializes it,
and use the X-PgBeam-Timestamp value verbatim so your recomputed signature
matches the bytes we signed. Compare with a constant-time function. Rejecting
deliveries whose X-PgBeam-Timestamp is far from the current time (5 minutes is
a reasonable window) is what closes the replay gap, so enforce it when you verify
v2.
The v1 header is still valid if you have not migrated. It signs the body only, so it cannot bind the timestamp:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(
rawBody: string,
signatureHeader: string,
secret: string,
): boolean {
const expected =
"sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(signatureHeader);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}SIEM authentication
Splunk HEC and Datadog deliveries are not HMAC-signed. They authenticate with
the destination's own token instead: Splunk uses an Authorization: Splunk <token> header and Datadog uses DD-API-KEY. Set that token as the endpoint's
secret.
Delivery and retries
PgBeam expects a 2xx within a few seconds. A non-2xx or a timeout is
retried with exponential backoff. Make your receiver idempotent by keying on
the event id, which is stable across retries.
SIEM formats
For a SIEM, set the endpoint's format and PgBeam shapes each event the way that
product ingests it. The signing and retry behavior is the same.
| Format | Sends |
|---|---|
splunk_hec | Splunk HTTP Event Collector (HEC) envelope. Set your HEC token as the secret. |
datadog | Datadog Logs intake payload with ddsource: pgbeam and event tags. |
elastic | Elastic / OpenSearch JSON documents with an ECS-style shape. |
For splunk_hec and datadog, the endpoint secret is the destination's own
token (Splunk sends it as Authorization: Splunk <token>, Datadog as
DD-API-KEY) rather than an HMAC signature.
pgbeam webhooks create https://splunk.example.com:8088/services/collector \
--format splunk_hec \
--secret "$SPLUNK_HEC_TOKEN" \
--event query_blocked,budget_exhausted,kill_switch,anomaly_alertRelated
- Webhook events: every event type and its payload shape.
- Audit log: the queryable history these events come from.
- Anomaly detection: source of
anomaly_alertevents. - Approvals: source of
approval_requestedevents. - Safe migrations: source of
migration_flaggedevents.