Agent skill

RingCentral Webhooks Skill

Receive and verify RingCentral webhooks. Use when setting up RingCentral webhook subscriptions, completing the Validation-Token handshake, checking the Verification-Token header, or handling message-store, presence, and telephony session events.

Install this skill

npx skills add hookdeck/webhook-skills --skill ringcentral-webhooks


When to Use This Skill

  • How do I receive RingCentral webhooks?
  • How do I complete the RingCentral Validation-Token handshake?
  • How do I verify RingCentral notifications with a Verification-Token?
  • How do I create a RingCentral subscription (POST /restapi/v1.0/subscription)?
  • Why is my RingCentral subscription getting blacklisted?
  • How do I handle message-store, presence, or telephony/sessions events?

Verification (core)

RingCentral does not HMAC-sign webhooks and does not follow the Standard Webhooks spec. Authenticity relies on two mechanisms:

  1. Validation-Token handshake (mandatory). When a subscription is created or renewed, RingCentral sends a request carrying a Validation-Token request header. Your handler must echo that exact value back in a Validation-Token response header and return 200 — fast (within a few seconds). No body is required.
  2. Verification-Token (optional). Set an arbitrary verificationToken string on the subscription. RingCentral then sends it as a Verification-Token header on every notification. Compare it (timing-safe) to reject spoofed requests.

Node:

const crypto = require('crypto');

// Timing-safe string compare for the Verification-Token header.
function tokenMatches(received, expected) {
  const a = Buffer.from(received || '', 'utf8');
  const b = Buffer.from(expected || '', 'utf8');
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

// In your POST handler:
const validationToken = req.get('Validation-Token');
if (validationToken) {                          // 1. handshake — echo + 200
  res.set('Validation-Token', validationToken);
  return res.status(200).json({ status: 'ok' });
}
if (EXPECTED_TOKEN && !tokenMatches(req.get('Verification-Token'), EXPECTED_TOKEN)) {
  return res.status(401).json({ error: 'Invalid verification token' }); // 2. auth
}

Python:

import hmac

# 1. handshake — echo the Validation-Token back and return 200:
#    if validation_token: return Response(headers={"Validation-Token": validation_token})
# 2. optional per-notification auth:
def token_matches(received: str, expected: str) -> bool:
    return hmac.compare_digest(received or "", expected or "")

For complete handlers with route wiring, event dispatch, and tests, see:

Common Event Types

RingCentral events are identified by the event filter (an API resource path) in the notification's event field, not by a short name. Common filters:

Event filterTriggered When
/restapi/v1.0/account/~/extension/~/message-storeNew message (SMS, voicemail, fax)
/restapi/v1.0/account/~/extension/~/message-store/instant?type=SMSInbound SMS (instant)
/restapi/v1.0/account/~/extension/~/presenceExtension presence changes
/restapi/v1.0/account/~/telephony/sessionsCall (telephony session) lifecycle
/restapi/v1.0/account/~/extension/~/telephony/sessionsPer-extension call events
/restapi/v1.0/account/~/extensionExtension created/updated/deleted

For the full event filter reference, see RingCentral Event Types.

Important Headers

HeaderDirectionDescription
Validation-Tokenrequest → responseHandshake token to echo back on subscribe/renew
Verification-TokenrequestYour configured token, sent on every notification

Environment Variables

# Optional shared secret; set as `verificationToken` when creating the subscription.
RINGCENTRAL_VERIFICATION_TOKEN=your_verification_token

Local Development

# Start tunnel (no account needed). Address must be HTTPS — the tunnel provides it.
npx hookdeck-cli listen 3000 ringcentral --path /webhooks/ringcentral

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Aug 4, 2026

View on GitHub →