# Webhook DELETE endpoint returns encrypted columns (PR #8643)

**URL:** <https://community.infisical.com/t/webhook-delete-endpoint-returns-encrypted-columns-pr-8643/90>\
**Category:** Contributing\
**Created:** [October 15, 2026, 2:40pm UTC](https://community.infisical.com/t/webhook-delete-endpoint-returns-encrypted-columns-pr-8643/90 "2026-10-15T14:40:02Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Arunkoo](https://yyz2.discourse-cdn.com/flex048/user_avatar/community.infisical.com/arunkoo/32/126_2.png) [@Arunkoo](https://community.infisical.com/u/Arunkoo)\
**Post date:** [October 15, 2026, 2:40pm UTC](https://community.infisical.com/t/webhook-delete-endpoint-returns-encrypted-columns-pr-8643/90/1 "2026-10-15T14:40:02Z")

</div>

Hi team! I opened a small backend fix and would appreciate a review when you have time:

> <https://github.com/Infisical/infisical/pull/8643>
>
> \## Context
> 
> While going through the webhook router I noticed the DELETE route (\`…DELETE /api/v1/webhooks/:webhookId\`) is the only one without a \`response\` schema. Because of that, Fastify doesn't filter anything and just sends back whatever \`deleteWebhook\` returns, which is the raw \`webhooks\` row.
> 
> So deleting a webhook currently returns fields that never show up on any other webhook route:
> 
> \- \`encryptedUrl\` and \`encryptedPassKey\` (serialized as Buffers)
> \- the old pre-KMS columns: \`urlCipherText\`, \`urlIV\`, \`urlTag\`, \`encryptedSecretKey\`, \`iv\`, \`tag\`, \`algorithm\`, \`keyEncoding\`
> \- the raw \`url\` column and the internal \`filteredEvents\` column
> 
> Everything there is still encrypted (and the legacy \`url\` column was blanked by the KMS migration), so nothing readable leaks. But it's internal data that shouldn't be part of the response, and it doesn't match the other routes.
> 
> The fix adds the same \`response\` schema PATCH already uses (\`sanitizedWebhookSchema\`). After this, DELETE returns the same fields as the other webhook routes.
> 
> A few things I checked so this doesn't break anyone:
> \- The status code (200) and the \`{ message, webhook }\` shape are unchanged.
> \- The frontend (\`useDeleteWebhook\`) doesn't read the response body.
> \- The DELETE audit log only uses \`environment.slug\`, \`id\`, \`isDisabled\` and \`secretPath\`, and all of those are still in the schema.
> \- I didn't add \`url\` (the way GET does), because \`deleteWebhook\` never decrypts it. The only \`url\` available there is the empty legacy column, so \`z.string()\` would fail on \`null\`.
> 
> I also added \`e2e-test/routes/v1/webhook.spec.ts\`. It checks the exact list of keys in the DELETE response rather than just checking that \`encryptedUrl\` is missing, so any column added later can't slip through this route unnoticed.
> 
> \## Screenshots
> 
> N/A, backend-only change.
> 
> \## Steps to verify the change
> 
> 1. Run the new spec: \`make test-api-e2e SPEC=webhook.spec\`. It passes.
> 2. Revert the router change and run it again. It fails and lists every leaked key (\`encryptedUrl\`, \`encryptedPassKey\`, \`urlCipherText\`, ...). I ran both locally.
> 3. Manual check: create a webhook in project settings, delete it, and look at the DELETE response in the network tab.
> 
> Before (trimmed):
> 
> \`\`\`json
> "webhook": {
> "id": "...",
> "secretPath": "/",
> "encryptedUrl": { "type": "Buffer", "data": \[244, 40, 124, "..."\] },
> "encryptedPassKey": { "type": "Buffer", "data": \[149, 210, "..."\] },
> "urlCipherText": null, "urlIV": null, "urlTag": null,
> "encryptedSecretKey": null, "iv": null, "tag": null,
> "algorithm": null, "keyEncoding": null, "url": null,
> "filteredEvents": \["secrets.modified", "..."\],
> "...": "..."
> }
> \`\`\`
> 
> After:
> 
> \`\`\`json
> "webhook": {
> "id": "...",
> "secretPath": "/",
> "lastStatus": null,
> "lastRunErrorMessage": null,
> "isDisabled": false,
> "createdAt": "...",
> "updatedAt": "...",
> "envId": "...",
> "type": "general",
> "projectId": "...",
> "environment": { "id": "...", "name": "Development", "slug": "dev" },
> "eventsFilter": \[{ "eventName": "secrets.modified" }, "..."\]
> }
> \`\`\`
> 
> I couldn't run \`lint:fix\` / \`type:check\` locally (my machine runs out of memory on them), so I'm relying on CI for those. The change is the same 6-line block PATCH uses, so I don't expect any issues.
> 
> \## Type
> 
> \- \[x\] Fix
> \- \[\] Feature
> \- \[\] Improvement
> \- \[\] Breaking
> \- \[\] Docs
> \- \[\] Chore
> 
> \## Checklist
> 
> \- \[x\] Title follows the \[conventional commit\](https://www.conventionalcommits.org/en/v1.0.0/#summary) format: \`type(scope): short description\` (scope is optional, e.g., \`fix: prevent crash on sync\` or \`fix(api): handle null response\`).
> \- \[x\] Tested locally
> \- \[\] Updated docs (if needed)
> \- \[\] Updated CLAUDE.md files (if needed)
> \- \[x\] Read the \[contributing guide\](https://infisical.com/docs/contributing/getting-started/overview)

`DELETE /api/v1/webhooks/:webhookId` was the only webhook route without a response schema, so it returned the **`raw DB row`** , including **`encryptedUrl`** , **`encryptedPassKey`** and the old **`pre-KMS cipher`** columns. The fix adds the same **`sanitizedWebhookSchema`** the PATCH route uses, so DELETE now returns the same fields as the other webhook routes. Status code and response shape are unchanged.

It’s a 6-line router change plus an e2e test that checks the exact response keys (fails without the fix, passes with it). Full details are in the PR description.

Happy to make any changes. Thanks!
