Skip to content

feat(webhooks): per-form scope, delivery status, Discord URL guard#197

Merged
Musiker15 merged 1 commit into
mainfrom
feat/webhook-per-form-and-status
Jul 9, 2026
Merged

feat(webhooks): per-form scope, delivery status, Discord URL guard#197
Musiker15 merged 1 commit into
mainfrom
feat/webhook-per-form-and-status

Conversation

@Musiker15

Copy link
Copy Markdown
Member

Follow-up to the Discord webhook format (#196), driven by two customer reports:

I set up a webhook but it didn't really log my new test submission. Also does this allow setting a different webhook per form?

1. Per-form webhooks (the "different webhook per form" ask)

A webhook can now be scoped to a single form instead of the whole guild. New nullable Webhook.formId (null = every form, the existing behaviour) is matched in enqueueWebhooks against the event's form, so a guild can route each form to its own endpoint or Discord channel. The add form gains a Form scope selector (default: all forms) and each row shows its scope.

2. Why "it didn't log" — two fixes

  • Delivery visibility: the webhooks page now shows each endpoint's last delivery outcome (delivered / failed with the error message / pending). A hook that isn't logging can now be diagnosed instead of failing silently.
  • Discord URL guard: pasting a Discord webhook URL while leaving the format on Generic JSON is now rejected with a clear message. That combination sends a raw HMAC-signed body that Discord rejects with a 400, which is the most likely reason the customer's test submission didn't appear.

Schema

Webhook.formId (+ FK ON DELETE CASCADE, index) and a (webhook_id, created_at DESC) index for the latest-delivery lookup. Migration applies via the deploy prisma migrate deploy.

Localized in all 7 languages. Added shared tests for the guard and the form scope (shared 92). Typecheck, lint, tests and build all green.

Note

The most probable cause of the customer's issue is the JSON-format-plus-Discord-URL combination, now blocked at validation. If their hook was a correct generic JSON endpoint, the new "last delivery" line on the page will show the actual failure (bad URL, non-2xx, timeout).

Follow-up to the Discord webhook format, addressing two customer reports.

Per-form webhooks: a webhook can now be scoped to a single form instead of the
whole guild. A new nullable Webhook.formId (null = every form) is matched in
enqueueWebhooks against the event's form, so a guild can route each form to its
own endpoint or Discord channel. The add form gains a "Form scope" selector
(default: all forms) and each row shows its scope.

Delivery visibility: the webhooks page now shows each endpoint's last delivery
outcome (delivered / failed with the error / pending), so a hook that isn't
logging can be diagnosed instead of failing silently.

Discord URL guard: pasting a Discord webhook URL while leaving the generic JSON
format is now rejected with a clear message. That combination posts a raw signed
body Discord rejects, which is the most likely reason a test submission "didn't
log".

Schema: Webhook.formId (+ FK, index) and a (webhook_id, created_at) index for
the latest-delivery lookup (+ migration). Localized in all 7 languages. Shared
tests for the guard and form scope (92). Typecheck, lint, test, build green.
@Musiker15 Musiker15 merged commit 1e1e455 into main Jul 9, 2026
3 checks passed
@Musiker15 Musiker15 deleted the feat/webhook-per-form-and-status branch July 9, 2026 18:25
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.

1 participant