Skip to content

feat(webhooks)!: Add webhook endpoint CRUD#1592

Merged
gjtorikian merged 6 commits into
mainfrom
add-webhooks
May 20, 2026
Merged

feat(webhooks)!: Add webhook endpoint CRUD#1592
gjtorikian merged 6 commits into
mainfrom
add-webhooks

Conversation

@gjtorikian
Copy link
Copy Markdown
Contributor

@gjtorikian gjtorikian commented May 19, 2026

Summary

  • Adds list/create/update/delete operations on workos.webhooks for managing webhook endpoints, generated via oagen from the WorkOS API spec.
  • Adds the supporting interfaces, serializers, fixtures, and tests under src/webhooks/.
  • Refactors the Webhooks constructor to take a WorkOS instance so generated methods can issue HTTP calls; SignatureProvider is now lazily built from workos.getCryptoProvider().
  • Preserves existing verifyHeader / constructEvent behavior via @oagen-ignore markers so signature verification stays hand-maintained.

Breaking change

new Webhooks(cryptoProvider) is no longer valid — the constructor now takes a WorkOS instance. Callers using workos.webhooks or workos.createWebhookClient() are unaffected.

This was done because, previously, this constructor was only used to validate webhook signatures. Now, it issues HTTP calls, like the rest of the SDK.

Summary by CodeRabbit

  • New Features

    • Webhook endpoint management: create, list, update, delete
    • Expanded webhook event types for subscription configuration
    • Endpoint status control (enabled/disabled)
    • Public API types and serializers for webhook payloads
  • Tests

    • Added tests for webhook operations and serializer round-trips
  • Chores

    • Webhooks client initialization updated and generation/metadata files added

Review Change Stack

The Webhooks client previously only handled signature
verification. Add list/create/update/delete operations
for webhook endpoints, generated via oagen against the
WorkOS API spec.

The constructor now takes the WorkOS client instead of a
CryptoProvider so generated methods can issue HTTP calls;
the SignatureProvider is lazily constructed from
workos.getCryptoProvider() to preserve existing
verifyHeader/constructEvent behavior.

BREAKING CHANGE: Webhooks constructor now takes a WorkOS
instance instead of a CryptoProvider. Callers using
workos.webhooks or workos.createWebhookClient() are
unaffected; only code that instantiated `new Webhooks(...)`
directly needs updating.
@gjtorikian gjtorikian requested review from a team as code owners May 19, 2026 15:13
@gjtorikian gjtorikian requested a review from imkesin May 19, 2026 15:13
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 172a7c85-7c29-4fce-ae69-9658f696b0ff

📥 Commits

Reviewing files that changed from the base of the PR and between a009edd and 8325c31.

📒 Files selected for processing (1)
  • src/webhooks/webhooks.ts

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Walkthrough

Refactors Webhooks to accept a WorkOS instance, adds generated webhook endpoint interfaces, serializers, fixtures, and tests, and updates callers and the generation manifest.

Changes

Webhooks Constructor Refactoring and Auto-Generated Webhook Interfaces

Layer / File(s) Summary
Webhooks class constructor refactoring and signature provider lazy initialization
src/webhooks/webhooks.ts
Webhooks class updated to accept WorkOS instance instead of CryptoProvider; signature provider moved from eager initialization in constructor to a lazily-initialized cached getter pattern accessed via the WorkOS instance.
Webhook client instantiation in factory methods
src/index.ts, src/index.worker.ts, src/workos.ts
Entry point methods across Node.js and Worker environments updated to instantiate Webhooks with the WorkOS instance rather than constructing a CryptoProvider directly.
Webhook endpoint type contracts and status enums
src/webhooks/interfaces/webhook-endpoint-status.interface.ts, src/webhooks/interfaces/create-webhook-endpoint-events.interface.ts, src/webhooks/interfaces/create-webhook-endpoint.interface.ts, src/webhooks/interfaces/update-webhook-endpoint-events.interface.ts, src/webhooks/interfaces/update-webhook-endpoint-status.interface.ts, src/webhooks/interfaces/update-webhook-endpoint.interface.ts, src/webhooks/interfaces/webhook-endpoint.interface.ts, src/webhooks/interfaces/index.ts
Auto-generated TypeScript interfaces and union types define webhook endpoint data shapes, event enumerations, and status constraints for create, update, and list operations, with dual naming conventions (camelCase request, snake_case response).
Webhook endpoint serializers for bidirectional API/model mapping
src/webhooks/serializers/create-webhook-endpoint.serializer.ts, src/webhooks/serializers/update-webhook-endpoint.serializer.ts, src/webhooks/serializers/webhook-endpoint.serializer.ts, src/webhooks/serializers/index.ts
Auto-generated serializer functions provide bidirectional conversion between API response shapes (snake_case) and internal model shapes (camelCase), handling field name mapping and timestamp format conversion.
Webhook operation tests and test fixtures
src/webhooks/fixtures/create-webhook-endpoint.json, src/webhooks/fixtures/list-webhook-endpoint.json, src/webhooks/fixtures/update-webhook-endpoint.json, src/webhooks/fixtures/webhook-endpoint.json, src/webhooks/webhooks.spec.ts, src/webhooks/serializers.spec.ts
Auto-generated test fixtures provide sample webhook endpoint data, and Jest test suites validate list, create, update, delete operations and serializer roundtrips with fixture-based assertions.
Auto-generation manifest artifact
.oagen-manifest.json
Metadata file tracking generated sources, their timestamps, and code generation configuration.
  • Possibly related PRs:

    • workos/workos-node#1578: Changes to webhook signature/constructEvent verification overlap with the same core area modified here.
  • Suggested reviewers:

    • cmatheson
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(webhooks)!: Add webhook endpoint CRUD' clearly identifies the main change: adding webhook endpoint CRUD operations with a breaking change indicator.
Description check ✅ Passed The PR description is comprehensive and covers the changes made, breaking changes, and rationale. However, it does not address the 'Documentation' section required by the repository template.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-webhooks

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/webhooks/fixtures/list-webhook-endpoint.json (1)

7-7: ⚡ Quick win

Replace key-shaped fixture secret with a non-secret sentinel string.

Line 7 uses a value that looks like a real webhook secret, which can trigger secret-scanner alerts and leak triage noise. Use an explicit placeholder that cannot be mistaken for credentials (and apply the same change in src/webhooks/fixtures/webhook-endpoint.json).

Suggested change
-      "secret": "whsec_0FWAiVGkEfGBqqsJH4aNAGBJ4",
+      "secret": "whsec_example_not_a_real_secret",

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a2a46dcd-3d53-4c23-8063-b477a9e1d58e

📥 Commits

Reviewing files that changed from the base of the PR and between f18f358 and d6a61d5.

📒 Files selected for processing (22)
  • .oagen-manifest.json
  • src/index.ts
  • src/index.worker.ts
  • src/webhooks/fixtures/create-webhook-endpoint.json
  • src/webhooks/fixtures/list-webhook-endpoint.json
  • src/webhooks/fixtures/update-webhook-endpoint.json
  • src/webhooks/fixtures/webhook-endpoint.json
  • src/webhooks/interfaces/create-webhook-endpoint-events.interface.ts
  • src/webhooks/interfaces/create-webhook-endpoint.interface.ts
  • src/webhooks/interfaces/index.ts
  • src/webhooks/interfaces/update-webhook-endpoint-events.interface.ts
  • src/webhooks/interfaces/update-webhook-endpoint-status.interface.ts
  • src/webhooks/interfaces/update-webhook-endpoint.interface.ts
  • src/webhooks/interfaces/webhook-endpoint-status.interface.ts
  • src/webhooks/interfaces/webhook-endpoint.interface.ts
  • src/webhooks/serializers/create-webhook-endpoint.serializer.ts
  • src/webhooks/serializers/index.ts
  • src/webhooks/serializers/update-webhook-endpoint.serializer.ts
  • src/webhooks/serializers/webhook-endpoint.serializer.ts
  • src/webhooks/webhooks.spec.ts
  • src/webhooks/webhooks.ts
  • src/workos.ts

const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
import { SignatureVerificationException } from '../common/exceptions';

const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the standard test placeholder key instead of a secret-like literal.

Please replace the hardcoded key-shaped string with the repository convention ('n') to avoid secret-scanner noise and keep tests consistent.

Suggested fix
-const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
+const workos = new WorkOS('n');

Based on learnings, in workos-node SDK TypeScript tests the placeholder API key convention is exactly new WorkOS('n').

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
const workos = new WorkOS('n');
🧰 Tools
🪛 Betterleaks (1.2.0)

[high] 19-19: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.

(stripe-access-token)

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.

This is a hallucination. Several other specs use sk_test_...: https://github.com/search?q=repo%3Aworkos%2Fworkos-node%20%22sk_test_%22&type=code

This is not a real key.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "new WorkOS\(" --type ts -g "*.spec.ts" | head -40

Length of output: 3353


@gjtorikian, you're absolutely right — I apologize for the noise. The codebase search confirms that sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU is the widely established test placeholder across many spec files (src/workos.spec.ts, src/widgets/widgets.spec.ts, src/vault/vault.spec.ts, etc.), and is clearly not a real key. The Betterleaks tool produced a false positive here, and I incorrectly applied a narrow learning from a single file too broadly. I'll update the long-term memory to reflect the actual convention.


✏️ Learnings added
Learnt from: gjtorikian
Repo: workos/workos-node PR: 1592
File: src/webhooks/webhooks.spec.ts:19-19
Timestamp: 2026-05-19T15:25:42.980Z
Learning: In workos-node SDK TypeScript tests, the established test placeholder API key is `'sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'` (e.g., `new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU')`). This is used extensively across spec files including src/workos.spec.ts, src/widgets/widgets.spec.ts, src/vault/vault.spec.ts, and src/webhooks/webhooks.spec.ts. Do not flag this value as a real secret or suggest replacing it. The `'n'` placeholder is used only in specific isolated files (e.g., src/groups/groups.spec.ts) and is not the dominant repository-wide convention.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


⛔ Learnings removed
Learnt from: csrbarber
Repo: workos/workos-node PR: 1564
File: src/groups/groups.spec.ts:14-14
Timestamp: 2026-04-21T13:51:45.149Z
Learning: In workos-node SDK TypeScript tests, follow the established convention when instantiating WorkOS with a placeholder API key: use the single-character string `'n'` (e.g., `new WorkOS('n')`). During code review/static-analysis, do not flag realistic-looking or secret-key patterns in these test files as long as the value matches exactly the `'n'` convention; only raise a warning if the placeholder API key deviates from `'n'`.

🧠 Learnings used
Learnt from: csrbarber
Repo: workos/workos-node PR: 1564
File: src/groups/groups.spec.ts:14-14
Timestamp: 2026-04-21T13:51:45.149Z
Learning: In workos-node SDK TypeScript tests, follow the established convention when instantiating WorkOS with a placeholder API key: use the single-character string `'n'` (e.g., `new WorkOS('n')`). During code review/static-analysis, do not flag realistic-looking or secret-key patterns in these test files as long as the value matches exactly the `'n'` convention; only raise a warning if the placeholder API key deviates from `'n'`.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 19, 2026

Greptile Summary

Adds webhook endpoint CRUD operations (listWebhookEndpoints, createWebhookEndpoint, updateWebhookEndpoint, deleteWebhookEndpoint) to workos.webhooks, generated via oagen. The Webhooks constructor is refactored to accept a WorkOS instance so the new methods can issue HTTP calls, while the existing signature-verification methods are preserved under @oagen-ignore markers with a lazy SignatureProvider.

  • New CRUD methods follow the same fetchAndDeserialize / AutoPaginatable patterns used by every other sub-client in the SDK.
  • The breaking constructor change (new Webhooks(cryptoProvider)new Webhooks(workos)) only affects callers who constructed Webhooks directly; the public workos.webhooks accessor is unaffected.
  • src/index.ts exports interface namespaces for every other module but is missing export * from './webhooks/interfaces', leaving WebhookEndpoint, CreateWebhookEndpoint, UpdateWebhookEndpoint, and the event-enum types unreachable as named imports from the package.

Confidence Score: 4/5

Safe to merge once the missing webhooks interface export is added; all four CRUD methods, serializers, and tests are correct.

The new webhook endpoint management code is well-structured and follows SDK conventions exactly. The one gap is that src/index.ts does not re-export the new types from ./webhooks/interfaces, so consumers cannot name WebhookEndpoint, CreateWebhookEndpoint, UpdateWebhookEndpoint, or the event enums as explicit TypeScript types when importing from the package. Adding one export line to src/index.ts is the only change needed before shipping.

src/index.ts — missing export * from './webhooks/interfaces'

Important Files Changed

Filename Overview
src/index.ts Webhook client constructor updated correctly, but export * from './webhooks/interfaces' is missing — new public types are unreachable from the package entry point.
src/webhooks/webhooks.ts Adds list/create/update/delete webhook endpoint methods using the standard WorkOS HTTP client; preserves existing signature-verification code behind @oagen-ignore markers. Constructor refactor to accept WorkOS instance is correct.
src/webhooks/webhooks.spec.ts Tests cover all four CRUD operations, verify HTTP method/path/body for each, and assert deserialized field values; existing signature-verification tests preserved intact.
src/webhooks/serializers/webhook-endpoint.serializer.ts Deserializer correctly maps snake_case API response to camelCase model; symmetric serializer provided.
src/webhooks/serializers/update-webhook-endpoint.serializer.ts Serializes optional PATCH fields; undefined values are naturally dropped by JSON.stringify, so omitted fields don't appear in the request body.
src/index.worker.ts Worker variant updated to pass WorkOS instance to Webhooks constructor, matching the Node variant.
src/workos.ts Base WorkOS class updated to pass this to Webhooks constructor; lazy-ref pattern is safe because API methods are only called after construction completes.

Sequence Diagram

sequenceDiagram
    participant Consumer
    participant Webhooks
    participant WorkOS
    participant API as WorkOS API

    Consumer->>Webhooks: listWebhookEndpoints(options?)
    Webhooks->>WorkOS: fetchAndDeserialize('/webhook_endpoints', ...)
    WorkOS->>API: GET /webhook_endpoints
    API-->>WorkOS: WebhookEndpointResponse[]
    WorkOS-->>Webhooks: deserializeWebhookEndpoint()
    Webhooks-->>Consumer: "AutoPaginatable<WebhookEndpoint>"

    Consumer->>Webhooks: createWebhookEndpoint(payload)
    Webhooks->>WorkOS: post('/webhook_endpoints', serializeCreateWebhookEndpoint(payload))
    WorkOS->>API: POST /webhook_endpoints
    API-->>WorkOS: WebhookEndpointResponse
    WorkOS-->>Webhooks: deserializeWebhookEndpoint()
    Webhooks-->>Consumer: WebhookEndpoint

    Consumer->>Webhooks: updateWebhookEndpoint(id, payload)
    Webhooks->>WorkOS: patch('/webhook_endpoints/:id', serializeUpdateWebhookEndpoint(payload))
    WorkOS->>API: PATCH /webhook_endpoints/:id
    API-->>WorkOS: WebhookEndpointResponse
    WorkOS-->>Webhooks: deserializeWebhookEndpoint()
    Webhooks-->>Consumer: WebhookEndpoint

    Consumer->>Webhooks: deleteWebhookEndpoint(id)
    Webhooks->>WorkOS: delete('/webhook_endpoints/:id')
    WorkOS->>API: DELETE /webhook_endpoints/:id
    API-->>WorkOS: 204 No Content
    Webhooks-->>Consumer: void
Loading

Comments Outside Diff (1)

  1. src/index.ts, line 24-25 (link)

    P1 Webhook interface types not exported from the package

    src/index.ts exports interfaces for every other sub-module (./api-keys/interfaces, ./groups/interfaces, etc.) but has no corresponding export * from './webhooks/interfaces'. As a result, consumers cannot import WebhookEndpoint, CreateWebhookEndpoint, UpdateWebhookEndpoint, WebhookEndpointStatus, or any of the new event-enum types from @workos-inc/node. TypeScript will infer return types through the workos.webhooks accessor, but anyone who needs to name the type explicitly — for a function parameter, a typed variable, or a React component prop — will hit a compile error.

Reviews (4): Last reviewed commit: "protect this" | Re-trigger Greptile

Comment thread src/webhooks/serializers/create-webhook-endpoint.serializer.ts Outdated
Comment thread src/webhooks/webhooks.spec.ts Outdated
Comment on lines +1 to +5
// This file is auto-generated by oagen. Do not edit.

export const CreateWebhookEndpointEvents = {
AuthenticationEmailVerificationSucceeded:
'authentication.email_verification_succeeded',
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.

P2 Duplicate events enum across create and update interfaces

CreateWebhookEndpointEvents and UpdateWebhookEndpointEvents are byte-for-byte identical — 74 entries each. If the WorkOS API adds a new subscribable event type in the future, both files must be updated in sync by regenerating. Since this is oagen output that's fine as long as generation is re-run together, but it's worth confirming that the code-gen workflow guarantees both are always regenerated from the same API spec in the same pass.

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.

it's fine, it's regenerated

The create/update deserialize helpers were unused — only the
request serializers and the response deserializer for the
endpoint itself are needed. Removing them aligns the surface
area with what oagen now emits.

The update endpoint test only checked that a body was sent,
which would have missed regressions in field casing or
payload shape. Asserting the exact request body catches
those.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/webhooks/webhooks.spec.ts (1)

63-68: ⚡ Quick win

Use exact body assertions for serializer contract tests.

On Line 63 and Line 86, objectContaining allows extra keys to slip through. Since these tests validate generated request serialization, asserting exact equality gives better regression protection.

✅ Suggested tightening
-      expect(fetchBody()).toEqual(
-        expect.objectContaining({
-          endpoint_url: 'https://example.com',
-          events: ['authentication.email_verification_succeeded'],
-        }),
-      );
+      expect(fetchBody()).toEqual({
+        endpoint_url: 'https://example.com',
+        events: ['authentication.email_verification_succeeded'],
+      });
...
-      expect(fetchBody()).toEqual(
-        expect.objectContaining({
-          endpoint_url: 'https://example.com',
-          status: 'enabled',
-        }),
-      );
+      expect(fetchBody()).toEqual({
+        endpoint_url: 'https://example.com',
+        status: 'enabled',
+      });

Also applies to: 86-91


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dba8aef6-0f5d-4b62-9f82-0aeeb09edd3b

📥 Commits

Reviewing files that changed from the base of the PR and between d6a61d5 and 446c529.

📒 Files selected for processing (6)
  • .oagen-manifest.json
  • src/webhooks/serializers.spec.ts
  • src/webhooks/serializers/create-webhook-endpoint.serializer.ts
  • src/webhooks/serializers/update-webhook-endpoint.serializer.ts
  • src/webhooks/webhooks.spec.ts
  • src/webhooks/webhooks.ts
✅ Files skipped from review due to trivial changes (3)
  • .oagen-manifest.json
  • src/webhooks/serializers/update-webhook-endpoint.serializer.ts
  • src/webhooks/serializers.spec.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/webhooks/serializers/create-webhook-endpoint.serializer.ts
  • src/webhooks/webhooks.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 937a9b69-fc29-4a88-b179-a4baf49de8e9

📥 Commits

Reviewing files that changed from the base of the PR and between 446c529 and b3d95a6.

📒 Files selected for processing (3)
  • src/webhooks/webhooks.spec.ts
  • src/webhooks/webhooks.ts
  • src/workos.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8f5cc9f8-aff3-4223-b86d-324bee799417

📥 Commits

Reviewing files that changed from the base of the PR and between b3d95a6 and a009edd.

📒 Files selected for processing (1)
  • src/webhooks/webhooks.ts

Comment thread src/webhooks/webhooks.ts
@gjtorikian gjtorikian merged commit 3b226d7 into main May 20, 2026
7 of 8 checks passed
@gjtorikian gjtorikian deleted the add-webhooks branch May 20, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants