refactor(notifications): replace webhook KeyMap with Handlebars templating #2039
+305
−137
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
This PR refactors the webhook notification agent to use Handlebars templating instead of the custom KeyMap system, providing a foundation for standardized templating across all notification agents.
Key Changes
server/lib/notifications/templateEngine.ts) — Centralized template rendering with custom helperseq,ne,lt,gt,lte,gte), logical (and,or), and string formatting helpers (capitalize,upper,lower,truncate,json,default){{#if}},{{#each}}, etc.) while still validating plain JSONNew Templating Capabilities
Users can now use advanced template features in webhook payloads:
{{#if media}}...{{/if}},{{#if (eq status "PENDING")}}...{{/if}}{{#if (gt request_count 10)}}...{{/if}}{{capitalize user_name}},{{upper media_type}},{{truncate subject 50}}{{default notifyuser_username "Unknown User"}}Backward Compatibility
✅ Fully backward compatible — all existing
{{variable}}syntax works unchanged✅ No configuration changes required for existing webhook setups
✅ Existing webhook payloads continue to work without modification
Testing
Instructions to replicate my tests and test yourself
1. Setup Webhook.site
2. Configure Webhook in Seerr
pnpm devhttp://localhost:5055/settings/notifications/webhook{ "type": "{{notification_type}}", "subject": "{{subject}}", "message": "{{message}}", "user": "{{notifyuser_username}}", "media_id": "{{media_tmdbid}}", "request_id": "{{request_id}}" }3. Test Notifications
Click the Test button on the webhook settings page. This will:
4. Verify Results
Check your webhook testing endpoint (webhook.site, etc.) and verify:
{{notification_type}}→"TEST_NOTIFICATION")5. Test Advanced Handlebars Features and Edge Cases
Test these with actual requests:
Test Case 1: Handlebars Helpers
{ "type": "{{notification_type}}", "subject": "{{subject}}", "user": { "name": "{{capitalize notifyuser_username}}", "email": "{{lower notifyuser_email}}" }, {{#if media}} "media": { "id": "{{media_tmdbid}}", "type": "{{upper media_type}}", "status": "{{media_status}}" }, {{/if}} {{#if request}} "request": { "id": "{{request_id}}", "requested_by": "{{requestedBy_username}}", "has_request": true }, {{else}} "request": null, {{/if}} "timestamp": "{{default current_date 'No date'}}" }This tests:
capitalizehelperlowerhelperupperhelper{{#if}}...{{/if}}conditionals{{#if}}...{{else}}...{{/if}}with else branchdefaulthelperTest Case 2: Nested Conditionals
{ "type": "{{notification_type}}", {{#if media}} {{#if request}} "media_request": { "media_id": "{{media_tmdbid}}", "request_id": "{{request_id}}" }, {{/if}} {{/if}} "message": "{{subject}}" }Test Case 3: Truncate Helper
Find a long example for this. I used "Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb"
{ "notification": "{{notification_type}}", "subject": "{{subject}}", "truncated_subject": "{{truncate subject 30}}", "user": "{{notifyuser_username}}", "comparison": { "original_length": "Full title shown above", "truncated_length": "Limited to 30 characters with ellipsis" } }Test Case 4: Comparison Helper
{ "type": "{{notification_type}}", "checks": { "is_test": "{{#if (eq notification_type 'TEST_NOTIFICATION')}}yes{{else}}no{{/if}}", "has_media": "{{#if media}}yes{{else}}no{{/if}}" } }Test Case 5: Webhook URL with Variables
Change your URL to something like this:
Manual Testing Completed
Manually tested with multiple scenarios:
Future Work
See plan here #1883
Screenshot
N/A — Backend refactor (frontend validation updated but UI unchanged)
To-Dos
pnpm buildpnpm i18n:extract— No new translation keys addedIssues Fixed or Closed
Part of the larger effort to enable custom email templates. This PR lays the groundwork for webhook template support that will be extended to all notification agents in future PRs.