Repository navigation
Webhook dashboard: recent-events and delivery-URL endpoints (#1393) - #1571
Conversation
…de-ai#1393) GET /api/connectors/:id/webhook/events wraps the existing WebhookEventsService.recentEvents() for the dashboard, scoped by organizationId and gated like any other connector setting (owner or admin). GET /api/connectors/:id/webhook/url returns the delivery URL to paste into the provider, built server-side from SERVER_URL the same way oauthRedirectUri() builds the OAuth callback. Plan limits are intentionally left out per the maintainer's note on HelpCode-ai#1393; the dashboard UI is a follow-up once the endpoint shape is confirmed.
CI's amcp-cli-operations check caught that packages/cli/src/generated/operations.ts was stale after adding the two webhook GET endpoints.
|
Thanks @rangarajan19, the endpoint shape looks good to us. On your question: owner-or-admin via We also checked the rest: both routes take the organization from the connector, |
Adds the connector page's "Inbound webhooks" card: a switch to opt the connector in, the delivery URL to copy, the signing secret (write-only, never read back), and the last ten verified deliveries. Calls the recent-events and delivery-URL endpoints from the earlier backend PR; carries no logic of its own about gating, retention, or the per-event cap.
|
@helpcodeai Implemented the UI — the "Inbound webhooks" card is now in the same PR, built from your 4 notes and confirmed against them in the description above. Marked it ready for review. |
helpcodeai
left a comment
There was a problem hiding this comment.
Thanks @rangarajan19, this is close. We read the whole diff: both routes check assertCanWrite first, the organization always comes from the connector, the secret is write-only and never sent back, and the card renders event fields as plain text. No security problem found.
Two small changes before we merge:
- Show the card only where it applies. Right now it appears on every connector page (Etsy, Odoo, databases, File Search...), and the text says Stripe. Please render
WebhookCardonly when the connector is a Stripe connector (connector.config?.adapterSlug === 'stripe') or whenwebhookEnabledis already on, so someone who turned it on through the API can still see and turn it off. - Leave the payload out of the dashboard response. The card only shows type, id and time, but
GET :id/webhook/eventsreturns each full payload (up to 256 KB each, with customer data). Please map the events to{ id, provider, eventType, externalId, receivedAt }in the controller. The MCP tool keeps the full payload as it is now. A test that checkspayloadis not in the response would be good.
The branch is also behind main; a merge from main when you push is enough.
…rd endpoint - Render WebhookCard only for a Stripe connector or one that already has webhookEnabled on, instead of every connector type. - GET :id/webhook/events now returns event metadata only (id, provider, eventType, externalId, receivedAt); the payload stays out of the dashboard response. The MCP tool's own read path is untouched and still returns the full payload. - Test asserting payload is absent from the dashboard response.
|
@helpcodeai Addressed both:
Also merged |
helpcodeai
left a comment
There was a problem hiding this comment.
Thanks @rangarajan19, both points are done: the card now shows only on Stripe connectors or when webhooks are already on, and the dashboard events no longer carry the payload. We checked the routes again (organization from the connector, owner or admin only, limit capped). Merging now.
Backend and UI for #1393's point 2.
What's here
GET /api/connectors/:id/webhook/events— wraps the existingWebhookEventsService.recentEvents(). Scoped byconnector.organizationId(never a client-supplied value); the service's own query filters by it too.GET /api/connectors/:id/webhook/url— the delivery URL to paste into the provider, built server-side fromSERVER_URL, the same wayGET /api/connectors/oauth/redirect-uribuilds the OAuth callback.webhook-card.tsx— the connector page's "Inbound webhooks" card: a switch forwebhookEnabled, the delivery URL with a copy button, a write-only field for the signing secret (never read back, likeauthConfig), and the last ten deliveries with a refresh button. It calls only the endpoints above, so it carries no gating, retention, or cap logic of its own.Against your 4 notes
organizationId; gated byassertCanWrite, confirmed as the intended check.oauthRedirectUri().Testing
packages/backend/src/connectors/connectors.controller.spec.ts: 9 new tests (SERVER_URL built correctly + localhost fallback, viewer rejected, non-owner member rejected, cross-org rejected,limit/sincepassed through correctly).npx tsc --noEmitandeslintclean on every touched file, backend and frontend.WebhookEventsServiceto the controller's constructor required updating 8 spec files that buildConnectorsControllerpositionally with mocks.docs/operations/observability.md;llms-full.txtregenerated.de.json,it.json).