Gareth Wilson Gareth Wilson

Guide to Rubrik Webhooks: Features and Best Practices

Published


Rubrik Security Cloud webhooks push events out of RSC as they happen: a backup fails, an anomaly is detected, an archive or replication job finishes, or a user changes an SLA domain. If you route Rubrik events into an incident tool, a SIEM, a chat channel or your own automation, webhooks are how they get there without polling the RSC API.

This guide covers how RSC webhooks are created, the default and custom payload formats, how to authenticate deliveries that Rubrik does not sign, Rubrik's short retry policy, and the best practices for running a Rubrik receiver in production.

What are Rubrik webhooks?

An RSC webhook sends events to a URL. Each webhook subscribes to either workload events or audit events, filtered by event type, object type and severity. You create webhooks in the RSC console or with the RSC GraphQL API.

The default payload is PagerDuty Common Event Format (PD-CEF), with a short summary at the top level and the event's details in custom_details. You can replace it with a custom JSON template, for example to match what Datadog or another tool expects.

Rubrik does not sign webhook requests. To authenticate them, you configure a credential on the webhook, such as a custom header, that Rubrik sends with each delivery.

Rubrik webhook features

FeatureDetails
ConfigurationRSC console, or the createWebhookV2 GraphQL mutation
SubscriptionsWorkload events or audit events, filtered by event type, object type and severity
PayloadPD-CEF by default, or a custom JSON template
Event classTop-level class in PD-CEF, title case on the wire (for example Backup)
Event IDcustom_details.id, the activity ID
AuthenticationNo signature. CUSTOM_HEADER, BEARER, BASIC, OAUTH2 or URL
Retries3, at 1 second, 5 seconds and 10 seconds. Then the event is removed from the queue
Auto-disableAfter 25 consecutive failures
Event history90 days in RSC, queryable with GraphQL

Rubrik does not document the HTTP method, the content type, a response timeout or source IP addresses for webhook deliveries.

Common events

Webhook subscriptions choose event types from RSC's EventType enum, such as BACKUP, ANOMALY, ARCHIVE, REPLICATION, RECOVERY, SECURITY_VIOLATION, THREAT_HUNT and UPGRADE. The enum has over 70 values.

In a PD-CEF delivery, the event's class arrives in the class field in title case. A BACKUP subscription produces bodies with "class": "Backup". The specific event is in custom_details.eventName, such as Snapshot.BackupFailed.

Here is Rubrik's example of a failed backup in the default format:

{
  "summary": "Failed backup of vSphere VM 'sh1-EncryptMe-05-Group1'.",
  "source": "Rubrik Security Cloud",
  "severity": "critical",
  "timestamp": "2024-07-18T06:39:40.46Z",
  "class": "Backup",
  "custom_details": {
    "seriesId": "ccd7a8a5-4c58-4c88-bff9-7bdffddb6099",
    "id": "c2b47274-6323-4025-b307-afed1cfb7574",
    "type": "Event",
    "objectId": "83c4a80a-4a57-5699-b399-651089135586",
    "objectName": "sh1-EncryptMe-05-Group1",
    "objectType": "VmwareVm",
    "status": "Failure",
    "clusterId": "6a271636-9392-4cba-90c5-bdbe227854ab",
    "clusterName": "sh1-PaloAlto",
    "eventName": "Snapshot.BackupFailed",
    "errorId": "Snapshot.VmwareSnapshotError",
    "location": "sh1-paloalto-vcsa.rubrikdemo.com"
  }
}

custom_details.id identifies this activity, and custom_details.seriesId is the activity series ID, which Rubrik suggests for grouping related events.

Custom templates

A custom template is freeform JSON with variables, for example:

{
  "title": "{{.ActivityType}} - {{.Status}}",
  "message": "{{.Message}}",
  "class": "{{.Class}}",
  "id": "{{.UUID}}",
  "aggregationKey": "{{.ActivitySeriesID}}",
  "timestamp": "{{.Time.UTC.Format \"1970-01-01T00:00:00Z\"}}"
}

If you use a template, keep {{.UUID}} and {{.Class}} in it, so your receiver can still deduplicate and route.

Setting up Rubrik webhooks

Generate a long random secret for the webhook, for example with openssl rand -hex 32. Then create the webhook in the RSC console, or with the API:

mutation {
  createWebhookV2(input: {
    payload: {
      name: "events-to-receiver"
      description: "Rubrik events for our receiver"
      url: "https://example.com/webhooks/rubrik"
      subscriptionType: {
        eventSubscription: {
          objectTypes: []
          isSubscribedToAllObjectTypes: true
          eventTypes: [BACKUP, ANOMALY]
          severities: [SEVERITY_CRITICAL]
        }
      }
      providerType: CUSTOM
      authInfo: {
        authType: CUSTOM_HEADER
        customHeaders: {
          headerKey: "Authorization"
          headerValue: "Bearer YOUR_GENERATED_SECRET"
        }
      }
    }
  }) {
    webhook { name id status }
    errorInfo { errorMessage statusCode }
  }
}

Use CUSTOM_HEADER authentication. It is the only mode whose wire form Rubrik documents: the header name and value you set are sent as they are. Rubrik also lists BEARER, BASIC, OAUTH2 and URL, but doesn't document what they look like on the request.

To list your webhooks and their last error, query allWebhooksV2.

Securing Rubrik webhooks

With no signature, verification means checking the header you configured. Compare the whole value with the one you stored, in constant time, and fail closed if your secret isn't configured.

const crypto = require('crypto');
const express = require('express');

// The headerKey you set on the Rubrik webhook. Node lowercases incoming header names.
const SECRET_HEADER = (process.env.RUBRIK_WEBHOOK_HEADER || 'authorization').toLowerCase();

function verifyRubrikHeader(headers, expected) {
  const received = headers[SECRET_HEADER];
  if (typeof received !== 'string' || !expected) return false;

  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

app.post('/webhooks/rubrik', express.raw({ type: '*/*' }), (req, res) => {
  // The full headerValue, for example "Bearer <secret>".
  const secret = process.env.RUBRIK_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500); // fail closed

  if (!verifyRubrikHeader(req.headers, secret)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  // Deduplicate on event.custom_details.id, route on event.class, then process async.
  res.sendStatus(200);
});

The same check in Python:

import hmac


def verify_rubrik_header(headers, expected: str, header_name: str = "authorization") -> bool:
    received = headers.get(header_name)
    if not received or not expected:
        return False
    return hmac.compare_digest(received.encode("utf-8"), expected.encode("utf-8"))

This check proves the caller knows your secret. It doesn't prove the body is unchanged, and the header is the same on every delivery, so a captured request could be sent again.

Make Rubrik webhooks production-ready. Hookdeck Event Gateway checks your Rubrik header before anything reaches your code, stores every event durably, and retries far beyond Rubrik's three quick attempts.

Rubrik webhook limitations and pain points

Three retries in 16 seconds

The Problem: A brief outage or a slow deploy on your side means lost backup failures and security alerts.

Why It Happens: Rubrik retries a failed delivery 3 times, after 1, 5 and 10 seconds. After that, "the event will be removed from the queue".

Workarounds:

  • Return a 2xx as soon as the header checks out, and do the work afterwards.
  • After an outage, query activitySeriesConnection or userAuditConnection for the window you missed. RSC keeps 90 days of events.

How Hookdeck Can Help: Hookdeck accepts each event as soon as it arrives and stores it, so Rubrik's first attempt succeeds even while your service is down. You then set your own retry policy and replay anything that failed.

A failing endpoint gets switched off

The Problem: After a longer outage, Rubrik stops sending to the webhook and nothing arrives until someone notices.

Why It Happens: A webhook is auto-disabled after 25 consecutive failures.

Workarounds:

  • Check webhook status and lastFailedErrorInfo with allWebhooksV2, and alert when a webhook is disabled.
  • Re-enable the webhook once your receiver is healthy.

How Hookdeck Can Help: Hookdeck acknowledges deliveries even while your service is down, so your outage doesn't count against the webhook. Issues alert you when delivery to your service starts failing.

No signature, only a static credential

The Problem: A webhook without authentication accepts anything posted to its URL, and a captured request carries a credential that never changes.

Why It Happens: Rubrik signs nothing. Its authentication modes send a credential to your receiver, and only the custom header mode is documented on the wire.

Workarounds:

  • Use CUSTOM_HEADER with a long random value, over HTTPS.
  • Never log the header value.
  • To rotate, accept both values for a short time, update the webhook, then remove the old value.

How Hookdeck Can Help: Hookdeck's Rubrik source type checks a header for you. Enter the header name and the exact value, including any Bearer prefix, and Hookdeck rejects requests without it before they reach your service.

The class in the body doesn't match the subscription name

The Problem: A router that matches BACKUP never sees a backup event.

Why It Happens: Subscriptions use EventType enum values such as BACKUP, but PD-CEF bodies carry the class in title case, such as Backup.

Workarounds:

  • Route on the title-case class value, or compare case-insensitively.
  • Use custom_details.eventName when you need the specific event.

How Hookdeck Can Help: Hookdeck filters can route on class, severity or custom_details.status, so failures go to your incident tool and successes go to reporting.

Best practices

Deduplicate on the activity ID

Retries resend the same event. Store custom_details.id (or {{.UUID}} in a custom template) and skip repeats. See our guide to webhook idempotency.

Keep the default format unless you need a template

PD-CEF already includes the IDs you need. If a downstream tool needs a different shape, consider transforming events on the way rather than changing the template for every consumer. Hookdeck transformations can do that.

Subscribe narrowly

Filter by event type, object type and severity so your receiver handles only what it acts on.

Acknowledge fast and process asynchronously

With retries that end after about 16 seconds, return 200 straight away and queue the work. See why to process webhooks asynchronously.

Conclusion

Rubrik Security Cloud sends workload and audit events in PD-CEF or a custom template, with no signature and a retry policy that gives up after three attempts in about 16 seconds. Protect each webhook with a CUSTOM_HEADER credential, compare it in full, route on the title-case class, deduplicate on custom_details.id, and query the RSC API to fill any gaps.

Hookdeck Event Gateway verifies your header and stops requests that fail it, stores every event before your service sees it, and retries on your terms, so a short outage doesn't lose a backup failure or a security alert.

Get started with Hookdeck for free and handle Rubrik webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.