VPN webhooks are signed HTTP POSTs that TeleQuick sends when VPN sessions connect, close or get rejected, and when credentials are revoked. They run on the same unified webhook plane as every other vertical (webhooks.* procedures), with the endpoint’s vertical set to vpn. The console screen at VPN → Webhooks is a thin client over those procedures: it creates endpoints, renders the subscribable event groups from the server-side registry, sends signed test pings, and shows the delivery log with redelivery.

What you can subscribe to

The event-group picker is not hardcoded in the console. It renders from webhooks.catalog, which returns the BFF’s webhook registry keyed by vertical. That means the picker can never offer a token that no producer emits, and the registry is the authoritative list of vpn.* event types for your build of the platform. Read it directly to enumerate the tokens:
Each group has the shape: When you create or update an endpoint you submit event types, not group keys — the console expands the checked groups into their types before calling webhooks.create. create and update both run assertKnownEventTypes, so a token that is not in the registry is rejected as a bad request.

Session lifecycle events

The VPN group set covers the session lifecycle: a session connecting, a session closing, and a connection attempt being rejected. A rejection event is what you subscribe to if you need to see attempts that never became a session, rather than only completed sessions. The exact token for each of these — and any additional session events in your build — comes from the types array of the corresponding group in webhooks.catalog. Subscribe by group rather than by literal token where you can: group membership is maintained server-side, so an endpoint subscribed to a whole group keeps working when the group gains a type.

Credential and access events

The second VPN concern is credentials: TeleQuick emits an event when a VPN credential is revoked. Revocation events are the ones that security tooling normally cares about most, because they are the signal that an issued credential can no longer be used — pair them with the session events above to reconstruct “who could connect, and who did”. As with session events, take the literal tokens from the credential group’s types in the catalog response.

Event type matching

The dispatcher matches a delivery’s event type against the endpoint’s event_types array using three forms, and the console applies the same contract when it decides which group labels to show on an endpoint row: If you tick every group in the picker, the console collapses the selection to ['*'] rather than listing the current tokens. That is a deliberate difference in behaviour: a * endpoint receives future event types automatically, while an endpoint pinned to explicit tokens does not.

Create an endpoint and store the signing secret

webhooks.create takes the org, the vertical, the URL, an optional description, the event types, and the private-egress flag:
Input constraints enforced by the procedure and the console form: The response contains the signing secret in cleartext once. The row stores it sealed, plus a signing_secret_preview (the first 12 characters) for display. The console shows the secret in a copy-once card; if you lose it, your only option is webhooks.rotateSecret.

Verify the signature

Every POST carries a signature header:
where v1 is hex(hmac_sha256(secret, "<t>.<body>")). Compute it over the raw request body, before any JSON parsing or re-serialisation, and compare with a constant-time equality check. HTTP header names are case-insensitive, so match the header without regard to case.

Test sends

webhooks.sendTest delivers a signed test.ping to one endpoint immediately and returns the outcome synchronously: Use it to prove signature verification works on your side before you depend on real events. The test delivery is written to the delivery log like any other.

Delivery log and redelivery

webhooks.deliveries.list is the delivery log. It is org-scoped and takes optional filters: Pass vertical: 'vpn' rather than filtering client-side. A busy org with heavy voice or streams traffic can fill an unfiltered page entirely with other verticals’ rows. Each row carries created_at, event_type, endpoint_id, status, attempts, last_status_code and last_error. Payloads are deliberately excluded from the list because they are large — the console fetches the full payload separately when you open a delivery’s detail drawer. webhooks.deliveries.redeliver({ orgId, id }) queues a row again. The console offers it for rows in delivered or dead status — that is, for ones that are no longer in flight.

Pause, resume and rotate

webhooks.update covers the operational controls:
  • status: 'paused' stops deliveries to the endpoint.
  • status: 'active' resumes it and resets consecutive_failures, which is how you clear an endpoint the platform auto-paused into the failing state. The endpoint list shows that state as a failing pill together with the consecutive-failure count.
  • url and eventTypes can be changed in place. A new URL is re-validated against the row’s own allow_private_egress flag, so an endpoint created as public-only cannot be edited into an http:// target.
webhooks.rotateSecret mints a new secret and seals it in place, returning the cleartext once. The old secret stops verifying immediately, so stage the new secret on your receiver first — the console’s confirmation dialog says the same thing. Every one of create, update, rotate and delete records an audit entry (webhooks.endpoint.create, .update, .rotate_secret) against the endpoint id with the acting user.

On-prem and private-network endpoints

By default an endpoint must be https:// and must resolve to a deliverable public target. Tick On-prem / private-network endpoint (allowPrivateEgress: true) when the receiver is a collector inside your own network: that flag is what permits RFC1918 targets and http:// URLs. Without it, create rejects an http:// URL with a BAD_REQUEST. The flag is a property of the endpoint row, not of the request, and the console marks such rows with private egress under the URL.

Wiring deliveries into a SIEM

A practical shape for security tooling:
  1. Create one vertical: 'vpn' endpoint per collector, and put the collector’s name in description — the console shows the description in place of the secret preview, which makes the endpoint list readable during an incident.
  2. Subscribe to all groups (*) if the SIEM should see every VPN event including types added later; subscribe to the session and credential groups explicitly if you want a fixed, reviewable set.
  3. Verify the signature over the raw body and reject unsigned or mismatched requests at the collector’s edge.
  4. Set allowPrivateEgress if the collector is on-prem.
  5. Monitor the failing state and dead deliveries. Poll webhooks.deliveries.list with status: 'dead' and vertical: 'vpn'; the console polls the log every 15 s and surfaces the same rows.
  6. Backfill gaps with webhooks.deliveries.redeliver once the collector is healthy again.
  • Authentication — API keys for calling the control-plane procedures
  • Telemetry — metrics and traces alongside webhook deliveries