Skip to content

feat(backend): Add directory group webhook event types - #10378

Merged
mauricioabreu merged 2 commits into
mainfrom
mauricio-antunes/directory-group-webhook-types
Nov 3, 2026
Merged

mauricioabreu merged 2 commits into
mainfrom
mauricio-antunes/directory-group-webhook-types

Conversation

@mauricioabreu

@mauricioabreu mauricioabreu commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Description

Directory Sync sends directoryGroup.created, .updated, .deleted and directoryGroupMembership.created, .deleted, but WebhookEvent has no members for them, so verifyWebhook() cannot narrow them and handlers have to cast the payload.

Adds DirectoryGroupWebhookEvent and DirectoryGroupMembershipWebhookEvent to the union, with their payload types. The deleted membership carries cause (group_deleted or member_removed). The membership payload has no id, so it declares id?: never to keep event.data.id typed as string | undefined across the union, which an existing type test guards.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@changeset-bot

changeset-bot Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 31566d2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@clerk/backend Minor
@clerk/astro Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/hono Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/swingset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
clerk-js-sandbox Ready Ready Preview Nov 3, 2026 7:14pm UTC
swingset Ready Ready Preview Nov 3, 2026 7:14pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Walkthrough

The backend adds payload interfaces and webhook event types for directory groups and directory group memberships. The event types join the WebhookEvent union, and the new payload and event types are exported from the package. Type-level tests check event membership and payload fields, including deletion causes and nullable user IDs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: jescalan

Merge Risk: Minimal · up to 31566

No concrete merge-blocking defect remains established for this type-only webhook addition.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title names the primary change: adding directory group webhook event types to the backend package. The changeset adds DirectoryGroupWebhookEvent and DirectoryGroupMembershipWebhookEvent to the Web…
Description check Passed The description states the problem (Directory Sync sends directory group and membership events that WebhookEvent does not cover, so verifyWebhook() cannot narrow them). It then lists the added union m…
Docstring Coverage Passed Docstring coverage is inconclusive: files from earlier reviews still need to be rechecked within the check's analysis limits. Not blocking.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the primary change: adding directory group webhook event types to the backend package. The changeset adds DirectoryGroupWebhookEvent and DirectoryGroupMembershipWebhookEvent to the WebhookEvent union, along with their payload types, so the title matches the main change. It omits the membership events and the synced event, but that level of detail is not required.

Full details: Description check

Explanation

The description states the problem (Directory Sync sends directory group and membership events that WebhookEvent does not cover, so verifyWebhook() cannot narrow them). It then lists the added union members, the payload types, the cause field on deleted membership events, and the id?: never choice. All of this matches the changeset files. The description is related to the changeset in substance.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Linked repositories: Some configured repositories could not be included in this review. Cross-repository analysis may be incomplete. Please check linked-repository access and settings.

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@pkg-pr-new

pkg-pr-new Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@10378

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@10378

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@10378

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@10378

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@10378

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@10378

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@10378

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@10378

@clerk/expo-biometrics

npm i https://pkg.pr.new/@clerk/expo-biometrics@10378

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@10378

@clerk/expo-native-components

npm i https://pkg.pr.new/@clerk/expo-native-components@10378

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@10378

@clerk/express

npm i https://pkg.pr.new/@clerk/express@10378

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@10378

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@10378

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@10378

@clerk/mosaic

npm i https://pkg.pr.new/@clerk/mosaic@10378

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@10378

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@10378

@clerk/react

npm i https://pkg.pr.new/@clerk/react@10378

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@10378

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@10378

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@10378

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@10378

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@10378

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@10378

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@10378

commit: 31566d2

The directoryGroup.* and directoryGroupMembership.* events Directory Sync sends are now in the public webhook reference, but verifyWebhook() could not narrow them because WebhookEvent had no members for them. The membership payload declares an optional id it never sets, so event.data.id keeps its type across the union.
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-11-03T19:20:16.321Z

Summary

Metric Count
Packages analyzed 20
Packages with changes 1
🔴 Breaking changes 0
🟡 Non-breaking changes 2
🟢 Additions 20

🤖 This report was reviewed by claude-sonnet-4-6.


@clerk/backend

Current version: 3.26.1
Recommended bump: MINOR → 3.27.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: WebhookEvent
- export type WebhookEvent = UserWebhookEvent | SessionWebhookEvent | EmailWebhookEvent | EmailDeliveryWebhookEvent | SMSWebhookEvent | OrganizationWebhookEvent | OrganizationDomainWebhookEvent | OrganizationMembershipWebhookEvent | OrganizationInvitationWebhookEvent | OrganizationInvitationAcceptedWebhookEvent | RoleWebhookEvent | PermissionWebhookEvent | WaitlistEntryWebhookEvent | BillingPaymentAttemptWebhookEvent | BillingSubscriptionWebhookEvent | BillingSubscriptionItemWebhookEvent;
+ export type WebhookEvent = UserWebhookEvent | SessionWebhookEvent | EmailWebhookEvent | EmailDeliveryWebhookEvent | SMSWebhookEvent | DirectoryGroupWebhookEvent | DirectoryGroupMembershipWebhookEvent | OrganizationWebhookEvent | OrganizationDomainWebhookEvent | OrganizationMembershipWebhookEvent | OrganizationInvitationWebhookEvent | OrganizationInvitationAcceptedWebhookEvent | RoleWebhookEvent | PermissionWebhookEvent | WaitlistEntryWebhookEvent | BillingPaymentAttemptWebhookEvent | BillingSubscriptionWebhookEvent | BillingSubscriptionItemWebhookEvent;

Static analyzer: Breaking change in type alias WebhookEvent: Type changed: import("@clerk/backend").BillingPaymentAttemptWebhookEvent|import("@clerk/backend").BillingSubscriptionItemWebhookEvent… → import("@clerk/backend").BillingPaymentAttemptWebhookEvent|import("@clerk/backend").BillingSubscriptionItemWebhookEvent…

🤖 AI review (reclassified as non-breaking) (95%): WebhookEvent is an output type (returned by verifyWebhook as Promise); adding new union variants DirectoryGroupWebhookEvent and DirectoryGroupMembershipWebhookEvent only widens the return type, which is safe for consumers who switch/narrow on 'type' — existing exhaustive checks may need updating, but existing well-typed code continues to compile.

🟢 Additions (18)

Click to expand 18 changes
Added: DeletedDirectoryGroupMembershipWebhookEventJSON
+ export interface DeletedDirectoryGroupMembershipWebhookEventJSON extends DirectoryGroupMembershipWebhookEventJSON

Added interface DeletedDirectoryGroupMembershipWebhookEventJSON

Added: DeletedDirectoryGroupMembershipWebhookEventJSON.cause
+ cause: 'group_deleted' | 'member_removed';

Added property DeletedDirectoryGroupMembershipWebhookEventJSON.cause

Added: DirectoryGroupMembershipWebhookEvent
+ export type DirectoryGroupMembershipWebhookEvent = Webhook<'directoryGroupMembership.created', DirectoryGroupMembershipWebhookEventJSON> | Webhook<'directoryGroupMembership.deleted', DeletedDirectoryGroupMembershipWebhookEventJSON>;

Added type alias DirectoryGroupMembershipWebhookEvent

Added: DirectoryGroupMembershipWebhookEventJSON
+ export interface DirectoryGroupMembershipWebhookEventJSON

Added interface DirectoryGroupMembershipWebhookEventJSON

Added: DirectoryGroupMembershipWebhookEventJSON.directory_id
+ directory_id: string;

Added property DirectoryGroupMembershipWebhookEventJSON.directory_id

Added: DirectoryGroupMembershipWebhookEventJSON.directory_user_id
+ directory_user_id: string;

Added property DirectoryGroupMembershipWebhookEventJSON.directory_user_id

Added: DirectoryGroupMembershipWebhookEventJSON.group
+ group: {
+         object: typeof ObjectType.DirectoryGroup;
+         id: string;
+         display_name: string;
+     };

Added property DirectoryGroupMembershipWebhookEventJSON.group

Added: DirectoryGroupMembershipWebhookEventJSON.id
+ id?: never;

Added property DirectoryGroupMembershipWebhookEventJSON.id

Added: DirectoryGroupMembershipWebhookEventJSON.object
+ object: typeof ObjectType.DirectoryGroupMembership;

Added property DirectoryGroupMembershipWebhookEventJSON.object

Added: DirectoryGroupMembershipWebhookEventJSON.updated_at
+ updated_at: number;

Added property DirectoryGroupMembershipWebhookEventJSON.updated_at

Added: DirectoryGroupMembershipWebhookEventJSON.user_id
+ user_id: string | null;

Added property DirectoryGroupMembershipWebhookEventJSON.user_id

Added: DirectoryGroupWebhookEvent
+ export type DirectoryGroupWebhookEvent = Webhook<'directoryGroup.created' | 'directoryGroup.updated' | 'directoryGroup.synced' | 'directoryGroup.deleted', DirectoryGroupWebhookEventJSON>;

Added type alias DirectoryGroupWebhookEvent

Added: DirectoryGroupWebhookEventJSON
+ export interface DirectoryGroupWebhookEventJSON

Added interface DirectoryGroupWebhookEventJSON

Added: DirectoryGroupWebhookEventJSON.directory_id
+ directory_id: string;

Added property DirectoryGroupWebhookEventJSON.directory_id

Added: DirectoryGroupWebhookEventJSON.display_name
+ display_name: string;

Added property DirectoryGroupWebhookEventJSON.display_name

Added: DirectoryGroupWebhookEventJSON.id
+ id: string;

Added property DirectoryGroupWebhookEventJSON.id

Added: DirectoryGroupWebhookEventJSON.object
+ object: typeof ObjectType.DirectoryGroup;

Added property DirectoryGroupWebhookEventJSON.object

Added: DirectoryGroupWebhookEventJSON.updated_at
+ updated_at: number;

Added property DirectoryGroupWebhookEventJSON.updated_at

Subpath ./webhooks

🟡 Non-breaking Changes (1)

Modified: WebhookEvent
- export type WebhookEvent = UserWebhookEvent | SessionWebhookEvent | EmailWebhookEvent | EmailDeliveryWebhookEvent | SMSWebhookEvent | OrganizationWebhookEvent | OrganizationDomainWebhookEvent | OrganizationMembershipWebhookEvent | OrganizationInvitationWebhookEvent | OrganizationInvitationAcceptedWebhookEvent | RoleWebhookEvent | PermissionWebhookEvent | WaitlistEntryWebhookEvent | BillingPaymentAttemptWebhookEvent | BillingSubscriptionWebhookEvent | BillingSubscriptionItemWebhookEvent;
+ export type WebhookEvent = UserWebhookEvent | SessionWebhookEvent | EmailWebhookEvent | EmailDeliveryWebhookEvent | SMSWebhookEvent | DirectoryGroupWebhookEvent | DirectoryGroupMembershipWebhookEvent | OrganizationWebhookEvent | OrganizationDomainWebhookEvent | OrganizationMembershipWebhookEvent | OrganizationInvitationWebhookEvent | OrganizationInvitationAcceptedWebhookEvent | RoleWebhookEvent | PermissionWebhookEvent | WaitlistEntryWebhookEvent | BillingPaymentAttemptWebhookEvent | BillingSubscriptionWebhookEvent | BillingSubscriptionItemWebhookEvent;

Static analyzer: Breaking change in type alias WebhookEvent: Type changed: import("@clerk/backend").BillingPaymentAttemptWebhookEvent|import("@clerk/backend").BillingSubscriptionItemWebhookEvent… → import("@clerk/backend").BillingPaymentAttemptWebhookEvent|import("@clerk/backend").BillingSubscriptionItemWebhookEvent…

🤖 AI review (reclassified as non-breaking) (95%): WebhookEvent is only used as the return type of verifyWebhook (output/Promise result) and as the source of WebhookEventType; adding new union members DirectoryGroupWebhookEvent and DirectoryGroupMembershipWebhookEvent to an output type widens what callers receive but does not break any existing well-typed consumer code — exhaustive switches would need updating but are not a compile error unless the consumer uses noUncheckedIndexedAccess/strict exhaustiveness helpers outside the type system.

🟢 Additions (2)

Added: DirectoryGroupMembershipWebhookEvent
+ export type DirectoryGroupMembershipWebhookEvent = Webhook<'directoryGroupMembership.created', DirectoryGroupMembershipWebhookEventJSON> | Webhook<'directoryGroupMembership.deleted', DeletedDirectoryGroupMembershipWebhookEventJSON>;

Added type alias DirectoryGroupMembershipWebhookEvent

Added: DirectoryGroupWebhookEvent
+ export type DirectoryGroupWebhookEvent = Webhook<'directoryGroup.created' | 'directoryGroup.updated' | 'directoryGroup.synced' | 'directoryGroup.deleted', DirectoryGroupWebhookEventJSON>;

Added type alias DirectoryGroupWebhookEvent


Report generated by Break Check

Last ran on 31566d2.

@NicolasLopes7 NicolasLopes7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

export type SMSWebhookEvent = Webhook<'sms.created', SMSMessageJSON>;

export type DirectoryGroupWebhookEvent = Webhook<
'directoryGroup.created' | 'directoryGroup.updated' | 'directoryGroup.deleted',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

directoryGroup.synced is also a current Directory Sync webhook and uses this same DirectoryGroupEventPayload in the canonical webhook spec; clerk_go registers and emits it. Because it is absent here, verifyWebhook() still cannot narrow that event, and evt.type === 'directoryGroup.synced' remains a type error. Please include it in this union and in the type test/changeset so the directory-group event family is complete.

~ 🤖

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a late codex comment that was just added...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Will update it tomorrow before merging

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, ty!

@blacksmith-sh

blacksmith-sh Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Matches verifyWebhook narrows directory group events

A cast-free verifyWebhook handler for all five directory events fails to compile before and compiles after; cause is only on the deleted membership and limited to its two values.

Merge-base

Handler typecheck

This PR 31566d2e

Handler typecheck

Signed events handled

Also checked · 1 match

Matches event.data.id stays string | undefined

Proof on 31566d2e · Did Proof get it right? Yes · No

View full Proof report

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional comments: 1
packages/backend/src/api/resources/Webhooks.ts-80-80: 1

80-80: Minor · Functional Correctness · Quick win

Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Represent each group event as a union member.

Extract<WebhookEvent, { type: 'directoryGroup.synced' }> resolves to never because this member contains four event names. (typescriptlang.org) Event-specific handlers using Extract therefore cannot accept synced events. The membership tests use this extraction pattern at Lines 32–33.

Use a separate Webhook union member for each group event and add an extraction test.

Review info
Run configuration
  • Source mode: Auto
  • Repository: clerk/javascript
  • Branch: main
  • File: .coderabbit.yaml
  • Commit: 42664a9
  • Layers: Repository YAML (base), Organization UI (inherited)
  • Review profile: ASSERTIVE
  • Plan: Team
  • Run ID: 52727f38-94ff-4c4a-a3d6-d384b92bf0ea
Commits

Reviewing files that changed from the base of the PR and between 5452ee4 and 31566d2.

Files selected for processing: 4
  • .changeset/directory-group-webhook-events.md
  • packages/backend/src/__tests__/directory-group-webhooks.test.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/api/resources/Webhooks.ts
Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

Included review availability: This review used your included allowance. 7 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 8 reviews per hour.

@mauricioabreu
mauricioabreu merged commit 11d5dfb into main Nov 3, 2026
59 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/directory-group-webhook-types branch November 3, 2026 19:31

This branch was successfully deployed

2 active deployments
Preview – swingset — 31566d2e Deployed Nov 3, 2026 by vercel[bot]
Preview – clerk-js-sandbox — 31566d2e Deployed Nov 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants