Skip to content

Conversation

@BilalG1
Copy link
Collaborator

@BilalG1 BilalG1 commented Oct 17, 2025

https://www.loom.com/share/abbd19d1deaa4b3d8f8ae9c1f2f9b098?sid=d105ee21-f380-4c37-85f1-a58610cfff9d

High-level PR Summary

This PR adds webhook testing functionality to the dashboard, allowing users to send test events to their configured webhook endpoints and verify they're working correctly. The implementation adds a new backend API endpoint that creates a stack.test event via Svix, verifies successful delivery, and provides clear feedback. The dashboard UI is enhanced with a test dialog that shows a preview of the test payload and displays success or error messages, with the test action accessible both from the endpoint creation flow and the action menu for existing endpoints.

⏱️ Estimated Review Time: 15-30 minutes

💡 Review Order Suggestion
Order File Path
1 packages/stack-shared/src/interface/admin-interface.ts
2 packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
3 packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
4 apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
5 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx

Need help? Join our Discord

Analyze latest changes


Important

Add functionality to test webhooks from the dashboard, including backend route and frontend UI updates.

  • Backend:
    • Adds POST route in send-test-webhook/route.tsx to handle test webhook requests.
    • Validates request and response schemas using yup.
    • Implements logic to send test webhook using svix and handle success/error cases.
  • Frontend:
    • Updates page-client.tsx to include a dialog for creating and testing endpoints.
    • Adds TestEndpointDialog component to handle the test webhook UI.
    • Modifies CreateDialog and ActionMenu to support triggering the test webhook.
  • Admin Interface:
    • Adds sendTestWebhook method to StackAdminInterface in admin-interface.ts.
    • Implements sendTestWebhook in _StackAdminAppImplIncomplete in admin-app-impl.ts.
    • Updates StackAdminApp interface in admin-app.ts to include sendTestWebhook method.

This description was created by Ellipsis for c9bc00c. You can customize this summary. It will automatically update as commits are pushed.

@vercel
Copy link

vercel bot commented Oct 17, 2025

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

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Nov 5, 2025 11:39am
stack-dashboard Ready Ready Preview Comment Nov 5, 2025 11:39am
stack-demo Ready Ready Preview Comment Nov 5, 2025 11:39am
stack-docs Ready Ready Preview Comment Nov 5, 2025 11:39am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces a complete webhook testing feature: a new POST endpoint that sends test webhook messages via Svix with delivery verification and retry logic, dashboard UI components for creating and testing endpoints with status feedback, and Admin interface methods to trigger test webhooks across multiple packages.

Changes

Cohort / File(s) Summary
Backend Test Webhook Route
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
New POST endpoint handler that validates admin auth and endpoint_id, initializes Svix client, creates a test webhook message, and retries up to 3 times to verify successful delivery before returning success/error status
Dashboard Webhook Management UI
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
Replaced FormDialog with controlled CreateDialog using react-hook-form, added TestEndpointDialog for sending and monitoring test webhooks with status states, extended Endpoints and ActionMenu components with test callback propagation and integrated test dialog state management
Admin Interface Layer
packages/stack-shared/src/interface/admin-interface.ts
Added sendTestWebhook() method to StackAdminInterface that POSTs to /internal/send-test-webhook and returns success/error status
Admin App Implementation
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
Added sendTestWebhook() method that wraps the interface call and returns Result with error handling and default error message fallback
Admin App Interface
packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
Added sendTestWebhook() method signature to StackAdminApp interface, accepting endpointId and returning Promise<Result<undefined, { errorMessage: string }>>

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Dashboard as Dashboard UI
    participant AdminAPI as Admin App
    participant Backend as Backend Route
    participant Svix as Svix Service

    User->>Dashboard: Click "Test" on endpoint
    activate Dashboard
    Dashboard->>Dashboard: Open TestEndpointDialog
    Dashboard->>AdminAPI: sendTestWebhook(endpointId)
    deactivate Dashboard

    activate AdminAPI
    AdminAPI->>Backend: POST /send-test-webhook {endpoint_id}
    deactivate AdminAPI

    activate Backend
    Backend->>Svix: Get endpoint
    Backend->>Svix: Create test message
    Note over Backend: Retry up to 3x
    Backend->>Svix: List message attempts
    Backend->>Svix: Verify successful delivery
    Backend-->>AdminAPI: { success: true }
    deactivate Backend

    activate AdminAPI
    AdminAPI-->>Dashboard: Result.ok()
    deactivate AdminAPI

    activate Dashboard
    Dashboard->>Dashboard: Show success status
    Dashboard->>User: Display test result & payload
    deactivate Dashboard
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

The changes span multiple layers with varied complexity: async retry logic in the backend route with Svix integration, UI state management with form validation and dialog coordination, and interface consistency across three packages. While individual interface additions are simple, the orchestration across components and error handling patterns require careful review.

Poem

🐰 A twitching nose tests the webhook flow,
Through Svix tunnels, fast and slow,
With retries dancing, three times through,
Our little endpoints shine so true! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "webhook testing" directly relates to the main objective of the pull request, which is to add webhook testing functionality across multiple layers of the codebase (backend API endpoint, dashboard UI, and admin interface methods). The title is concise and clearly conveys the primary change—allowing users to test webhook endpoints. While brief, it avoids vague or generic phrasing and accurately summarizes the core purpose of the changeset.
Description Check ✅ Passed The PR description includes the minimal required template element (the CONTRIBUTING.md reminder comment) and substantially exceeds the basic template requirements with comprehensive context. The description provides a high-level summary of webhook testing functionality, structured breakdown of changes across backend and frontend, estimated review time, suggested review order, and clear explanations of what each component adds. The description is well-organized, specific, and directly addresses the PR's objectives without being vague or off-topic, making it easy for reviewers to understand the scope and purpose of the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dashboard-test-webhook

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@BilalG1 BilalG1 requested a review from N2D4 October 17, 2025 21:07
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Oct 17, 2025
Copy link

@recurseml recurseml bot left a comment

Choose a reason for hiding this comment

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

Review by RecurseML

🔍 Review performed on b677e3f..c9bc00c

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (5)

apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
packages/stack-shared/src/interface/admin-interface.ts
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Summary

Adds webhook testing functionality that allows users to send test events to webhook endpoints and verify delivery through the dashboard.

Key Changes

  • Backend: New /internal/send-test-webhook API endpoint that sends a stack.test event to a specified endpoint and verifies delivery using Result.retry pattern
  • Frontend: Enhanced webhook management UI with test dialogs, success/error states, and post-creation test flow
  • Type-safe integration: Added sendTestWebhook method across interface, implementation, and type definition layers

Implementation Details

  • Uses SmartRouteHandler for the API endpoint (follows custom instruction ab376f82-1d3d-4684-a503-9fb6824b49c2)
  • Retries webhook delivery check up to 3 times to allow for async processing
  • Comprehensive error handling with user-friendly error messages
  • Dialog-based UI with payload preview and status feedback

Confidence Score: 4/5

  • This PR is safe to merge with minor style improvements recommended
  • Well-structured implementation following established patterns. One minor style issue: form submission should use runAsynchronouslyWithAlert per custom instructions. No logical errors or security concerns found.
  • page-client.tsx:117 should use runAsynchronouslyWithAlert for consistency with project conventions

Important Files Changed

File Analysis

Filename Score Overview
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx 4/5 New API endpoint for testing webhook delivery, uses SmartRouteHandler and Result.retry pattern correctly
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx 3/5 Adds test webhook UI with dialogs and forms; async handler doesn't use runAsynchronouslyWithAlert

Sequence Diagram

sequenceDiagram
    participant User as User (Dashboard)
    participant UI as Webhooks Page
    participant AdminApp as StackAdminApp
    participant API as Backend API
    participant Svix as Svix Service
    participant Endpoint as Webhook Endpoint
    
    User->>UI: Click "Test" on endpoint
    UI->>UI: Open TestEndpointDialog
    User->>UI: Click "Send test event"
    UI->>AdminApp: sendTestWebhook({endpointId})
    AdminApp->>API: POST /internal/send-test-webhook
    API->>Svix: getOrCreate(application)
    API->>Svix: endpoint.get(endpointId)
    alt Endpoint not found
        Svix-->>API: Error
        API-->>UI: {success: false, error_message}
        UI->>UI: Show error alert
    else Endpoint found
        API->>Svix: message.create(stack.test event)
        alt Message creation fails
            Svix-->>API: Error
            API->>API: captureError()
            API-->>UI: {success: false, error_message}
            UI->>UI: Show error alert
        else Message created
            API->>API: Result.retry(check attempts, 3 times)
            loop Retry up to 3 times
                API->>Svix: messageAttempt.listByMsg(messageId)
                Svix-->>API: Attempt list
                API->>API: Check if endpoint has successful attempt
            end
            alt Successful delivery
                API-->>UI: {success: true}
                UI->>UI: Show success alert
            else No successful delivery
                API-->>UI: {success: false, error_message}
                UI->>UI: Show error alert
            end
        end
    end
Loading

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

) : (
<Form {...form}>
<form
onSubmit={(e) => runAsynchronously(handleSubmit(e))}
Copy link
Contributor

Choose a reason for hiding this comment

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

style: use runAsynchronouslyWithAlert instead per custom instructions

Suggested change
onSubmit={(e) => runAsynchronously(handleSubmit(e))}
onSubmit={(e) => runAsynchronouslyWithAlert(handleSubmit(e))}

Context Used: Rule from dashboard - Use runAsynchronouslyWithAlert from @stackframe/stack-shared/dist/utils/promises for async butto... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
Line: 117:117

Comment:
**style:** use `runAsynchronouslyWithAlert` instead per custom instructions

```suggestion
            onSubmit={(e) => runAsynchronouslyWithAlert(handleSubmit(e))}
```

**Context Used:** Rule from `dashboard` - Use `runAsynchronouslyWithAlert` from `@stackframe/stack-shared/dist/utils/promises` for async butto... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))

How can I resolve this? If you propose a fix, please make it concise.

@patched-codes
Copy link

patched-codes bot commented Oct 17, 2025

Documentation Changes Required

1. docs/templates/concepts/webhooks.mdx

  1. Update the 'Testing webhooks locally' section to include information about the new sendTestWebhook method.
  2. Add a description of the built-in testing capability for webhooks.
  3. Include a code example demonstrating how to use the sendTestWebhook method.

Example addition:

### Testing webhooks locally

In addition to third-party services like Svix Playground and Webhook.site, you can now use the built-in `sendTestWebhook` method to test your webhooks locally. This method is available in the StackAdminApp interface and allows you to send a test webhook to a specified endpoint.

Here's an example of how to use the `sendTestWebhook` method:

```typescript
const result = await stackAdminApp.sendTestWebhook({
  endpointId: 'your-endpoint-id'
});

if (result.ok) {
  console.log('Test webhook sent successfully');
} else {
  console.error('Error sending test webhook:', result.error.errorMessage);
}

This new feature provides a convenient way to test your webhook endpoints directly from your application.


### 2. docs/templates/concepts/stack-app.mdx

No changes are required for this file. The existing documentation for StackAdminApp is limited, and the new `sendTestWebhook` method follows the same pattern as the existing `sendTestEmail` method, which is not documented either.

Please ensure these changes are reflected in the relevant documentation file.

Copy link
Contributor

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (1)

196-205: Fix copy: “Delete domain” → “Delete endpoint”

Dialog title mismatches the action (endpoints, not domains).

-      title="Delete domain"
+      title="Delete endpoint"
🧹 Nitpick comments (4)
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx (3)

78-87: Add small backoff and consider a few more retries to avoid false negatives

Immediate polling can race delivery; add a short delay between retries and consider 5 attempts.

Apply for example:

-    const attemptResult = await Result.retry(async () => {
-      const attempts = await svix.messageAttempt.listByMsg(
-        projectId,
-        messageResult.data.id,
-        { status: MessageStatus.Success }
-      );
-      const success = attempts.data.some(a => a.endpointId === body.endpoint_id);
-      return success ? Result.ok(undefined) : Result.error("No successful attempt found");
-    }, 3);
+    const attemptResult = await Result.retry(async () => {
+      // brief backoff to give delivery a chance to complete
+      await new Promise(r => setTimeout(r, 600));
+      const attempts = await svix.messageAttempt.listByMsg(
+        projectId,
+        messageResult.data.id,
+        { status: MessageStatus.Success }
+      );
+      const success = attempts.data.some(a => a.endpointId === body.endpoint_id);
+      return success ? Result.ok(undefined) : Result.error("No successful attempt found");
+    }, 5);

48-60: Optional: ensure the stack.test event type exists before sending

Sending usually works without pre-creating, but creating once improves catalog visibility and filters in the portal.

     const messageResult = await Result.fromPromise(svix.message.create(
       projectId,
       {
         eventType: "stack.test",
         payload: {
           type: "stack.test",
           data: {
             message: "Stack webhook test event triggered from the Stack dashboard.",
             endpointUrl: endpoint.url,
           },
         },
       },
     ));
+    // Optionally ensure the event type exists for catalog/filters (no-op if it already exists)
+    // Ignore conflicts/duplicates.
+    // await svix.eventType.create({ name: "stack.test", description: "Stack dashboard test event" }).catch(() => {});

Reference: Svix docs recommend adding event types ahead of time and show eventType.create usage. (docs.svix.com)


78-85: Optimize attempt listing by filtering server-side

The Svix SDK v1.76+ supports passing both status and endpointId to messageAttempt.listByMsg. Update to filter attempts server-side instead of scanning all results:

      const attempts = await svix.messageAttempt.listByMsg(
        projectId,
        messageResult.data.id,
-       { status: MessageStatus.Success }
+       { status: MessageStatus.Success, endpointId: body.endpoint_id }
      );
-     const success = attempts.data.some(a => a.endpointId === body.endpoint_id);
+     const success = attempts.data.length > 0;
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (1)

255-263: Disable “Send test event” while sending to avoid double-submits

Prevents duplicate requests if the user clicks multiple times.

-      okButton={{
-        label: 'Send test event',
-        onClick: async () => {
+      okButton={{
+        label: 'Send test event',
+        props: { disabled: status === 'sending' },
+        onClick: async () => {
           await sendTestEvent();
           return 'prevent-close';
         },
       }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b677e3f and c9bc00c.

📒 Files selected for processing (5)
  • apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx (1 hunks)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (7 hunks)
  • packages/stack-shared/src/interface/admin-interface.ts (1 hunks)
  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts (1 hunks)
  • packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
packages/template/**

📄 CodeRabbit inference engine (AGENTS.md)

When modifying the SDK copies, make changes in packages/template (source of truth)

Files:

  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
  • packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
  • packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
  • packages/stack-shared/src/interface/admin-interface.ts
  • apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
apps/backend/src/app/api/latest/**

📄 CodeRabbit inference engine (AGENTS.md)

apps/backend/src/app/api/latest/**: Organize backend API routes by resource under /api/latest (e.g., auth at /api/latest/auth/, users at /api/latest/users/, teams at /api/latest/teams/, oauth providers at /api/latest/oauth-providers/)
Use the custom route handler system in the backend to ensure consistent API responses

Files:

  • apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

For blocking alerts and errors in UI, do not use toast notifications; use alerts instead

Files:

  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Keep hover/click animations snappy; avoid pre-transition delays on hover and apply transitions after the action (e.g., fade-out on hover end)

Files:

  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
🧬 Code graph analysis (3)
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts (1)
packages/stack-shared/src/utils/errors.tsx (1)
  • throwErr (10-19)
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx (4)
apps/backend/src/route-handlers/smart-route-handler.tsx (1)
  • createSmartRouteHandler (209-294)
packages/stack-shared/src/schema-fields.ts (6)
  • yupObject (247-251)
  • adminAuthTypeSchema (483-483)
  • adaptSchema (330-330)
  • yupString (187-190)
  • yupNumber (191-194)
  • yupBoolean (195-198)
apps/backend/src/lib/webhooks.tsx (1)
  • getSvixClient (13-18)
packages/stack-shared/src/utils/errors.tsx (2)
  • captureError (126-134)
  • StackAssertionError (69-85)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (6)
packages/stack-shared/src/schema-fields.ts (1)
  • urlSchema (334-342)
packages/stack-ui/src/components/action-dialog.tsx (1)
  • ActionDialog (31-135)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
apps/dashboard/src/components/form-fields.tsx (1)
  • InputField (59-97)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/utils.tsx (1)
  • getSvixResult (12-32)
apps/dashboard/src/components/settings.tsx (1)
  • SettingCard (11-48)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: all-good
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: restart-dev-and-test
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: setup-tests
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: Security Check
🔇 Additional comments (13)
packages/stack-shared/src/interface/admin-interface.ts (1)

357-368: LGTM: interface method matches backend route and response contract

Payload key (endpoint_id) and return shape are consistent with the server. No issues.

packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts (1)

59-60: LGTM: API surface is consistent with Result-pattern used elsewhere

Signature and error mapping align with adjacent methods.

packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts (1)

467-477: LGTM: correct delegation and error mapping

Good use of throwErr fallback and consistent Result handling.

apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx (5)

61-75: Nice: captured send failure with context

Good use of captureError and StackAssertionError with cause and identifiers.


8-29: Schema and metadata look correct

Admin auth required, strict POST, and typed JSON response are aligned with our handler system.


34-47: Endpoint existence handling is clear

Returning a friendly message when the endpoint is missing is good UX for admins.


98-106: LGTM: success path

Consistent response shape with interface/clients.


1-7: Confirmed: MessageStatus is compatible with Svix 1.25.0

The Svix npm package version 1.25.0 exports the MessageStatus enum with members including Success, which is declared in the backend's package.json and used correctly at line 82 of the route file ({ status: MessageStatus.Success }). No compatibility issues exist.

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (5)

120-137: Good security UX: HTTP warning

Clear warning for insecure URLs in dev; aligns with our UI guidelines (alerts over toasts).


41-49: Form setup looks solid

yupResolver + onChange mode and defaults are correct; resets on close to keep state clean.


59-67: Endpoint create flow is straightforward

Creates via Svix SDK then shows confirmation with follow-up actions. Nice touch using onTestRequested.


321-326: Action menu addition is appropriate

“Test” action wires through to dialog; good cohesion with onTestEndpoint.


371-406: Provider wiring and test-dialog state handling look correct

Uses useSvixToken, refresh via counter, and controlled dialog state. LGTM.

@cursor
Copy link

cursor bot commented Oct 18, 2025

You have run out of free Bugbot PR reviews for this billing cycle. This will reset on November 10.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Comment on lines +79 to +84
const attempts = await svix.messageAttempt.listByMsg(
projectId,
messageResult.data.id,
{ status: MessageStatus.Success }
);
const success = attempts.data.some(a => a.endpointId === body.endpoint_id);
Copy link

Choose a reason for hiding this comment

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

Suggested change
const attempts = await svix.messageAttempt.listByMsg(
projectId,
messageResult.data.id,
{ status: MessageStatus.Success }
);
const success = attempts.data.some(a => a.endpointId === body.endpoint_id);
const attemptsResult = await Result.fromPromise(svix.messageAttempt.listByMsg(
projectId,
messageResult.data.id,
{ status: MessageStatus.Success }
));
if (attemptsResult.status === "error") {
return Result.error("Failed to list message attempts");
}
const attempts = attemptsResult.data;
const success = attempts.data?.some(a => a.endpointId === body.endpoint_id);

The svix.messageAttempt.listByMsg() call inside the retry function is not wrapped in error handling, which means exceptions would crash the retry logic instead of being caught and retried.

View Details

Analysis

Unhandled exception in Result.retry() for svix.messageAttempt.listByMsg() call

What fails: The svix.messageAttempt.listByMsg() call in send-test-webhook route handler (line 79-82) is not wrapped in Result.fromPromise(), causing unhandled exceptions to escape the retry logic

How to reproduce:

  1. Trigger webhook test with network failure or API rate limiting
  2. Svix client throws exception (e.g., network timeout, rate limit)
  3. Exception propagates out of Result.retry() function instead of being caught and retried

Result: Route handler crashes with unhandled promise rejection instead of graceful error handling. Retry logic bypassed completely.

Expected: All exceptions should be converted to Result.error() via Result.fromPromise() wrapper, allowing retry logic to handle failures properly (consistent with other Svix calls in same file at lines 35 and 48)

Evidence: Result.retry() function signature expects (attemptIndex: number) => Result<T> | Promise<Result<T>> but current code calls async function that can throw exceptions directly

const projectId = auth.tenancy.project.id;
const svix = getSvixClient();

await svix.application.getOrCreate({ uid: projectId, name: projectId });
Copy link

Choose a reason for hiding this comment

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

Suggested change
await svix.application.getOrCreate({ uid: projectId, name: projectId });
const appResult = await Result.fromPromise(svix.application.getOrCreate({ uid: projectId, name: projectId }));
if (appResult.status === "error") {
return {
statusCode: 200,
bodyType: "json",
body: {
success: false,
error_message: "Failed to create or retrieve application.",
},
};
}

The svix.application.getOrCreate() call is not wrapped in Result.fromPromise, which means exceptions thrown by the Svix SDK would crash the route handler instead of being handled gracefully.

View Details

Analysis

Unhandled exception in send-test-webhook route causes 500 errors

What fails: svix.application.getOrCreate() in /apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx:34 throws ApiException when Svix API calls fail, crashing the route handler

How to reproduce:

// Trigger with invalid Svix credentials or network issues
const svix = new Svix('invalid-api-key');
await svix.application.getOrCreate({ uid: 'test-project', name: 'test-project' });

Result: Throws ApiException with HTTP 401/network errors, causing route handler to return 500 instead of graceful error response

Expected: Should return structured error response like other Svix calls in the same file (lines 35, 48) that use Result.fromPromise()

Comment on lines +255 to +261
okButton={{
label: 'Send test event',
onClick: async () => {
await sendTestEvent();
return 'prevent-close';
},
}}
Copy link

Choose a reason for hiding this comment

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

The "Send test event" button in the TestEndpointDialog is not disabled during the async webhook test operation, allowing users to click it multiple times and trigger duplicate API requests.

View Details
📝 Patch Details
diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
index b34fe218..cdea27fe 100644
--- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
+++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
@@ -258,6 +258,7 @@ function TestEndpointDialog(props: { endpoint: Endpoint, open: boolean, onOpenCh
           await sendTestEvent();
           return 'prevent-close';
         },
+        props: { disabled: status === 'sending' }
       }}
       cancelButton={status === 'sending' ? false : { label: 'Cancel' }}
     >

Analysis

"Send test event" button allows multiple concurrent webhook API calls

What fails: TestEndpointDialog okButton remains enabled during async sendTestEvent() operation (status === 'sending'), allowing multiple simultaneous API requests

How to reproduce:

  1. Open webhook test dialog in dashboard
  2. Click "Send test event" button rapidly multiple times before first request completes
  3. Multiple API calls to stackAdminApp.sendTestWebhook() execute concurrently

Result: Multiple identical webhook test requests sent simultaneously instead of button being disabled during operation

Expected: Button should be disabled during sending state, consistent with cancelButton behavior which hides when status === 'sending'

Fix: Added props: { disabled: status === 'sending' } to okButton configuration, following existing pattern used elsewhere in codebase for disabled buttons

Comment on lines +59 to +66
const handleSubmit = form.handleSubmit(async (values) => {
const created = await svix.endpoint.create(appId, { url: values.url, description: values.description });
setCreatedEndpoint({
id: created.id,
url: created.url,
description: created.description,
});
});
Copy link

Choose a reason for hiding this comment

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

If svix.endpoint.create() fails, the error will be silently caught and logged but not displayed to the user, leaving them unaware that endpoint creation failed.

View Details
📝 Patch Details
diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
index b34fe218..55705902 100644
--- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
+++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
@@ -16,7 +16,7 @@ import { AppEnabledGuard } from "../app-enabled-guard";
 import { PageLayout } from "../page-layout";
 import { useAdminApp } from "../use-admin-app";
 import { getSvixResult } from "./utils";
-import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
+import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
 
 type Endpoint = {
   id: string,
@@ -114,7 +114,7 @@ function CreateDialog(props: {
       ) : (
         <Form {...form}>
           <form
-            onSubmit={(e) => runAsynchronously(handleSubmit(e))}
+            onSubmit={(e) => runAsynchronouslyWithAlert(handleSubmit(e))}
             className="space-y-4"
           >
             <Alert>

Analysis

Silent error handling in webhook endpoint creation leaves users without feedback

What fails: CreateDialog in apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx uses runAsynchronously() which catches svix.endpoint.create() errors but doesn't show them to users

How to reproduce:

  1. Navigate to webhook settings page
  2. Click "Create new endpoint"
  3. Enter invalid URL or trigger network failure
  4. Click "Create" button

Result: Form submission fails silently - errors logged to console but user sees no feedback, form remains in normal state

Expected: Error should be displayed to user via alert (like other forms) so they know endpoint creation failed

Fix applied: Changed runAsynchronously() to runAsynchronouslyWithAlert() to match pattern used in new-project creation and provide user error feedback

};
}

const attemptResult = await Result.retry(async () => {
Copy link

Choose a reason for hiding this comment

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

The Result.retry callback function is missing the required attemptIndex parameter. The function signature expects (attemptIndex: number) => Result<T>, but the code passes a function with no parameters.

View Details
📝 Patch Details
diff --git a/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx b/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
index c0b42dc7..26b4f6d9 100644
--- a/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
+++ b/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
@@ -75,7 +75,7 @@ export const POST = createSmartRouteHandler({
       };
     }
 
-    const attemptResult = await Result.retry(async () => {
+    const attemptResult = await Result.retry(async (_attemptIndex) => {
       const attempts = await svix.messageAttempt.listByMsg(
         projectId,
         messageResult.data.id,
diff --git a/apps/backend/src/lib/webhooks.tsx b/apps/backend/src/lib/webhooks.tsx
index cac56608..f6820c2d 100644
--- a/apps/backend/src/lib/webhooks.tsx
+++ b/apps/backend/src/lib/webhooks.tsx
@@ -46,7 +46,7 @@ async function sendWebhooks(options: {
 
 function createWebhookSender<T extends yup.Schema>(event: WebhookEvent<T>) {
   return async (options: { projectId: string, data: yup.InferType<typeof event.schema> }) => {
-    await Result.retry(async () => {
+    await Result.retry(async (_attemptIndex) => {
       try {
         return Result.ok(await sendWebhooks({
           type: event.type,

Analysis

Missing required parameter in Result.retry callback functions

What fails: Result.retry function calls in send-test-webhook/route.tsx:78 and webhooks.tsx:49 pass callback functions with no parameters, but the function signature requires (attemptIndex: number) => Result<T>

How to reproduce:

# TypeScript compilation should catch this with --strict mode:
cd apps/backend && pnpm run typecheck

Result: TypeScript parameter count mismatch error (function expects 1 parameter but receives 0)

Expected: Callback functions should declare the attemptIndex parameter per the TypeScript function signature - use _attemptIndex if not needed (matching pattern used elsewhere in codebase)

@github-actions github-actions bot assigned BilalG1 and unassigned N2D4 Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants