Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/using-jellyseerr/notifications/webhook.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ This is typically not needed. Please refer to your webhook provider's documentat

This value will be sent as an `Authorization` HTTP header.

### API Key (optional)

:::info
Uses the `X-API-Key` header, a de facto standard for webhook authentication.
:::

This value will be sent as an `X-API-Key` HTTP header.

### JSON Payload

Customize the JSON payload to suit your needs. Jellyseerr provides several [template variables](#template-variables) for use in the payload, which will be replaced with the relevant data when the notifications are triggered.
Expand Down
2 changes: 2 additions & 0 deletions jellyseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,8 @@ components:
type: string
authHeader:
type: string
apiKey:
type: string
jsonPayload:
type: string
supportVariables:
Expand Down
17 changes: 10 additions & 7 deletions server/lib/notifications/agents/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,16 +196,19 @@ class WebhookAgent
}

try {
const headers: Record<string, string> = {};

if (settings.options.authHeader) {
headers.Authorization = settings.options.authHeader;
}
if (settings.options.apiKey) {
headers['X-API-Key'] = settings.options.apiKey;
}

await axios.post(
webhookUrl,
this.buildPayload(type, payload),
settings.options.authHeader
? {
headers: {
Authorization: settings.options.authHeader,
},
}
: undefined
Object.keys(headers).length > 0 ? { headers } : undefined
);

return true;
Expand Down
1 change: 1 addition & 0 deletions server/lib/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ export interface NotificationAgentWebhook extends NotificationAgentConfig {
webhookUrl: string;
jsonPayload: string;
authHeader?: string;
apiKey?: string;
supportVariables?: boolean;
};
}
Expand Down
2 changes: 2 additions & 0 deletions server/routes/settings/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ notificationRoutes.post('/webhook', async (req, res, next) => {
),
webhookUrl: req.body.options.webhookUrl,
authHeader: req.body.options.authHeader,
apiKey: req.body.options.apiKey,
supportVariables: req.body.options.supportVariables ?? false,
},
};
Expand Down Expand Up @@ -333,6 +334,7 @@ notificationRoutes.post('/webhook/test', async (req, res, next) => {
),
webhookUrl: req.body.options.webhookUrl,
authHeader: req.body.options.authHeader,
apiKey: req.body.options.apiKey,
supportVariables: req.body.options.supportVariables ?? false,
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ const messages = defineMessages(
supportVariablesTip:
'Available variables are documented in the webhook template variables section',
authheader: 'Authorization Header',
apikey: 'API Key',
apikeyTip: 'API key will be sent as X-API-Key header',
validationJsonPayloadRequired: 'You must provide a valid JSON payload',
webhooksettingssaved: 'Webhook notification settings saved successfully!',
webhooksettingsfailed: 'Webhook notification settings failed to save.',
Expand Down Expand Up @@ -159,6 +161,7 @@ const NotificationsWebhook = () => {
webhookUrl: data.options.webhookUrl,
jsonPayload: data.options.jsonPayload,
authHeader: data.options.authHeader,
apiKey: data.options.apiKey,
supportVariables: data.options.supportVariables ?? false,
}}
validationSchema={NotificationsWebhookSchema}
Expand All @@ -171,6 +174,7 @@ const NotificationsWebhook = () => {
webhookUrl: values.webhookUrl,
jsonPayload: JSON.stringify(values.jsonPayload),
authHeader: values.authHeader,
apiKey: values.apiKey,
supportVariables: values.supportVariables,
},
});
Expand Down Expand Up @@ -229,6 +233,7 @@ const NotificationsWebhook = () => {
webhookUrl: values.webhookUrl,
jsonPayload: JSON.stringify(values.jsonPayload),
authHeader: values.authHeader,
apiKey: values.apiKey,
supportVariables: values.supportVariables ?? false,
},
});
Expand Down Expand Up @@ -344,6 +349,19 @@ const NotificationsWebhook = () => {
</div>
</div>
</div>
<div className="form-row">
<label htmlFor="apiKey" className="text-label">
{intl.formatMessage(messages.apikey)}
<div className="label-tip">
{intl.formatMessage(messages.apikeyTip)}
</div>
</label>
<div className="form-input-area">
<div className="form-input-field">
<Field id="apiKey" name="apiKey" type="text" />
</div>
</div>
</div>
<div className="form-row">
<label htmlFor="webhook-json-payload" className="text-label">
{intl.formatMessage(messages.customJson)}
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@
"components.Settings.Notifications.NotificationsSlack.webhookUrl": "Webhook URL",
"components.Settings.Notifications.NotificationsSlack.webhookUrlTip": "Create an <WebhookLink>Incoming Webhook</WebhookLink> integration",
"components.Settings.Notifications.NotificationsWebhook.agentenabled": "Enable Agent",
"components.Settings.Notifications.NotificationsWebhook.apikey": "API Key",
"components.Settings.Notifications.NotificationsWebhook.apikeyTip": "API key will be sent as X-API-Key header",
"components.Settings.Notifications.NotificationsWebhook.authheader": "Authorization Header",
"components.Settings.Notifications.NotificationsWebhook.customJson": "JSON Payload",
"components.Settings.Notifications.NotificationsWebhook.resetPayload": "Reset to Default",
Expand Down
Loading