diff --git a/authentik/api/v3/urls.py b/authentik/api/v3/urls.py index d6c58a3f83..87adf0b982 100644 --- a/authentik/api/v3/urls.py +++ b/authentik/api/v3/urls.py @@ -24,6 +24,7 @@ from authentik.core.api.users import UserViewSet from authentik.crypto.api import CertificateKeyPairViewSet from authentik.events.api.event import EventViewSet from authentik.events.api.notification import NotificationViewSet +from authentik.events.api.notification_mapping import NotificationWebhookMappingViewSet from authentik.events.api.notification_rule import NotificationRuleViewSet from authentik.events.api.notification_transport import NotificationTransportViewSet from authentik.flows.api.bindings import FlowStageBindingViewSet @@ -159,6 +160,7 @@ router.register("propertymappings/all", PropertyMappingViewSet) router.register("propertymappings/ldap", LDAPPropertyMappingViewSet) router.register("propertymappings/saml", SAMLPropertyMappingViewSet) router.register("propertymappings/scope", ScopeMappingViewSet) +router.register("propertymappings/notification", NotificationWebhookMappingViewSet) router.register("authenticators/duo", DuoDeviceViewSet) router.register("authenticators/static", StaticDeviceViewSet) diff --git a/authentik/events/api/notification_mapping.py b/authentik/events/api/notification_mapping.py new file mode 100644 index 0000000000..89fed5d9c5 --- /dev/null +++ b/authentik/events/api/notification_mapping.py @@ -0,0 +1,28 @@ +"""NotificationWebhookMapping API Views""" +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet + +from authentik.core.api.used_by import UsedByMixin +from authentik.events.models import NotificationWebhookMapping + + +class NotificationWebhookMappingSerializer(ModelSerializer): + """NotificationWebhookMapping Serializer""" + + class Meta: + + model = NotificationWebhookMapping + fields = [ + "pk", + "name", + "expression", + ] + + +class NotificationWebhookMappingViewSet(UsedByMixin, ModelViewSet): + """NotificationWebhookMapping Viewset""" + + queryset = NotificationWebhookMapping.objects.all() + serializer_class = NotificationWebhookMappingSerializer + filterset_fields = ["name"] + ordering = ["name"] diff --git a/authentik/events/api/notification_transport.py b/authentik/events/api/notification_transport.py index 7b3adc06dd..25d48f3dc9 100644 --- a/authentik/events/api/notification_transport.py +++ b/authentik/events/api/notification_transport.py @@ -38,6 +38,7 @@ class NotificationTransportSerializer(ModelSerializer): "mode", "mode_verbose", "webhook_url", + "webhook_mapping", "send_once", ] diff --git a/authentik/events/migrations/0018_auto_20210911_2217.py b/authentik/events/migrations/0018_auto_20210911_2217.py new file mode 100644 index 0000000000..66ce756563 --- /dev/null +++ b/authentik/events/migrations/0018_auto_20210911_2217.py @@ -0,0 +1,46 @@ +# Generated by Django 3.2.6 on 2021-09-11 22:17 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_core", "0028_alter_token_intent"), + ("authentik_events", "0017_alter_event_action"), + ] + + operations = [ + migrations.CreateModel( + name="NotificationWebhookMapping", + fields=[ + ( + "propertymapping_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="authentik_core.propertymapping", + ), + ), + ], + options={ + "verbose_name": "Notification Webhook Mapping", + "verbose_name_plural": "Notification Webhook Mappings", + }, + bases=("authentik_core.propertymapping",), + ), + migrations.AddField( + model_name="notificationtransport", + name="webhook_mapping", + field=models.ForeignKey( + default=None, + null=True, + on_delete=django.db.models.deletion.SET_DEFAULT, + to="authentik_events.notificationwebhookmapping", + ), + ), + ] diff --git a/authentik/events/models.py b/authentik/events/models.py index bab7d1bfd6..f4326cbaf7 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -2,7 +2,7 @@ from datetime import timedelta from inspect import getmodule, stack from smtplib import SMTPException -from typing import Optional, Union +from typing import TYPE_CHECKING, Optional, Type, Union from uuid import uuid4 from django.conf import settings @@ -15,7 +15,7 @@ from structlog.stdlib import get_logger from authentik import __version__ from authentik.core.middleware import SESSION_IMPERSONATE_ORIGINAL_USER, SESSION_IMPERSONATE_USER -from authentik.core.models import ExpiringModel, Group, User +from authentik.core.models import ExpiringModel, Group, PropertyMapping, User from authentik.events.geo import GEOIP_READER from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict from authentik.lib.sentry import SentryIgnoredException @@ -27,6 +27,8 @@ from authentik.tenants.models import Tenant from authentik.tenants.utils import DEFAULT_TENANT LOGGER = get_logger("authentik.events") +if TYPE_CHECKING: + from rest_framework.serializers import Serializer def default_event_duration(): @@ -220,6 +222,9 @@ class NotificationTransport(models.Model): mode = models.TextField(choices=TransportMode.choices) webhook_url = models.TextField(blank=True) + webhook_mapping = models.ForeignKey( + "NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None + ) send_once = models.BooleanField( default=False, help_text=_( @@ -239,15 +244,22 @@ class NotificationTransport(models.Model): def send_webhook(self, notification: "Notification") -> list[str]: """Send notification to generic webhook""" + default_body = { + "body": notification.body, + "severity": notification.severity, + "user_email": notification.user.email, + "user_username": notification.user.username, + } + if self.webhook_mapping: + default_body = self.webhook_mapping.evaluate( + user=notification.user, + request=None, + notification=notification, + ) try: response = get_http_session().post( self.webhook_url, - json={ - "body": notification.body, - "severity": notification.severity, - "user_email": notification.user.email, - "user_username": notification.user.username, - }, + json=default_body, ) response.raise_for_status() except RequestException as exc: @@ -414,3 +426,25 @@ class NotificationRule(PolicyBindingModel): verbose_name = _("Notification Rule") verbose_name_plural = _("Notification Rules") + + +class NotificationWebhookMapping(PropertyMapping): + """Modify the schema and layout of the webhook being sent""" + + @property + def component(self) -> str: + return "ak-property-mapping-notification-form" + + @property + def serializer(self) -> Type["Serializer"]: + from authentik.events.api.notification_mapping import NotificationWebhookMappingSerializer + + return NotificationWebhookMappingSerializer + + def __str__(self): + return f"Notification Webhook Mapping {self.name}" + + class Meta: + + verbose_name = _("Notification Webhook Mapping") + verbose_name_plural = _("Notification Webhook Mappings") diff --git a/schema.yml b/schema.yml index 446cc2d23b..b44c84e952 100644 --- a/schema.yml +++ b/schema.yml @@ -9388,6 +9388,219 @@ paths: $ref: '#/components/schemas/ValidationError' '403': $ref: '#/components/schemas/GenericError' + /propertymappings/notification/: + get: + operationId: propertymappings_notification_list + description: NotificationWebhookMapping Viewset + parameters: + - in: query + name: name + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + tags: + - propertymappings + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedNotificationWebhookMappingList' + description: '' + '400': + $ref: '#/components/schemas/ValidationError' + '403': + $ref: '#/components/schemas/GenericError' + post: + operationId: propertymappings_notification_create + description: NotificationWebhookMapping Viewset + tags: + - propertymappings + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookMappingRequest' + required: true + security: + - authentik: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookMapping' + description: '' + '400': + $ref: '#/components/schemas/ValidationError' + '403': + $ref: '#/components/schemas/GenericError' + /propertymappings/notification/{pm_uuid}/: + get: + operationId: propertymappings_notification_retrieve + description: NotificationWebhookMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Notification Webhook Mapping. + required: true + tags: + - propertymappings + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookMapping' + description: '' + '400': + $ref: '#/components/schemas/ValidationError' + '403': + $ref: '#/components/schemas/GenericError' + put: + operationId: propertymappings_notification_update + description: NotificationWebhookMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Notification Webhook Mapping. + required: true + tags: + - propertymappings + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookMappingRequest' + required: true + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookMapping' + description: '' + '400': + $ref: '#/components/schemas/ValidationError' + '403': + $ref: '#/components/schemas/GenericError' + patch: + operationId: propertymappings_notification_partial_update + description: NotificationWebhookMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Notification Webhook Mapping. + required: true + tags: + - propertymappings + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedNotificationWebhookMappingRequest' + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookMapping' + description: '' + '400': + $ref: '#/components/schemas/ValidationError' + '403': + $ref: '#/components/schemas/GenericError' + delete: + operationId: propertymappings_notification_destroy + description: NotificationWebhookMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Notification Webhook Mapping. + required: true + tags: + - propertymappings + security: + - authentik: [] + responses: + '204': + description: No response body + '400': + $ref: '#/components/schemas/ValidationError' + '403': + $ref: '#/components/schemas/GenericError' + /propertymappings/notification/{pm_uuid}/used_by/: + get: + operationId: propertymappings_notification_used_by_list + description: Get a list of all objects that use this object + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Notification Webhook Mapping. + required: true + tags: + - propertymappings + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UsedBy' + description: '' + '400': + $ref: '#/components/schemas/ValidationError' + '403': + $ref: '#/components/schemas/GenericError' /propertymappings/saml/: get: operationId: propertymappings_saml_list @@ -21368,6 +21581,10 @@ components: readOnly: true webhook_url: type: string + webhook_mapping: + type: string + format: uuid + nullable: true send_once: type: boolean description: Only send notification once, for example when sending a webhook @@ -21393,6 +21610,10 @@ components: $ref: '#/components/schemas/NotificationTransportModeEnum' webhook_url: type: string + webhook_mapping: + type: string + format: uuid + nullable: true send_once: type: boolean description: Only send notification once, for example when sending a webhook @@ -21410,6 +21631,34 @@ components: type: string required: - messages + NotificationWebhookMapping: + type: object + description: NotificationWebhookMapping Serializer + properties: + pk: + type: string + format: uuid + readOnly: true + title: Pm uuid + name: + type: string + expression: + type: string + required: + - expression + - name + - pk + NotificationWebhookMappingRequest: + type: object + description: NotificationWebhookMapping Serializer + properties: + name: + type: string + expression: + type: string + required: + - expression + - name OAuth2Provider: type: object description: OAuth2Provider Serializer @@ -23183,6 +23432,41 @@ components: required: - pagination - results + PaginatedNotificationWebhookMappingList: + type: object + properties: + pagination: + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + results: + type: array + items: + $ref: '#/components/schemas/NotificationWebhookMapping' + required: + - pagination + - results PaginatedOAuth2ProviderList: type: object properties: @@ -25525,10 +25809,22 @@ components: $ref: '#/components/schemas/NotificationTransportModeEnum' webhook_url: type: string + webhook_mapping: + type: string + format: uuid + nullable: true send_once: type: boolean description: Only send notification once, for example when sending a webhook into a chat channel. + PatchedNotificationWebhookMappingRequest: + type: object + description: NotificationWebhookMapping Serializer + properties: + name: + type: string + expression: + type: string PatchedOAuth2ProviderRequest: type: object description: OAuth2Provider Serializer diff --git a/web/src/elements/sidebar/SidebarBrand.ts b/web/src/elements/sidebar/SidebarBrand.ts index facfa54e86..d8d982f103 100644 --- a/web/src/elements/sidebar/SidebarBrand.ts +++ b/web/src/elements/sidebar/SidebarBrand.ts @@ -15,9 +15,9 @@ import AKGlobal from "../../authentik.css"; import { configureSentry } from "../../api/Sentry"; import { CurrentTenant } from "@goauthentik/api"; -import { ifDefined } from "lit-html/directives/if-defined"; import { EVENT_SIDEBAR_TOGGLE } from "../../constants"; import { tenant } from "../../api/Config"; +import { first } from "../../utils"; // If the viewport is wider than MIN_WIDTH, the sidebar // is shown besides the content, and not overlayed. @@ -99,7 +99,7 @@ export class SidebarBrand extends LitElement {
authentik icon diff --git a/web/src/flows/FlowExecutor.ts b/web/src/flows/FlowExecutor.ts index f86accf006..a3bb3a5ecc 100644 --- a/web/src/flows/FlowExecutor.ts +++ b/web/src/flows/FlowExecutor.ts @@ -45,11 +45,12 @@ import { ShellChallenge, } from "@goauthentik/api"; import { DEFAULT_CONFIG, tenant } from "../api/Config"; -import { ifDefined } from "lit-html/directives/if-defined"; import { until } from "lit-html/directives/until"; import { TITLE_DEFAULT } from "../constants"; import { configureSentry } from "../api/Sentry"; import { WebsocketClient } from "../common/ws"; +import { first } from "../utils"; +import { DefaultTenant } from "../elements/sidebar/SidebarBrand"; @customElement("ak-flow-executor") export class FlowExecutor extends LitElement implements StageHost { @@ -342,7 +343,10 @@ export class FlowExecutor extends LitElement implements StageHost {
authentik icon
diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 9ed6f5be27..62eea79bcb 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -1646,6 +1646,7 @@ msgstr "Export flow" #: src/pages/events/EventInfo.ts #: src/pages/policies/expression/ExpressionPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Expression" @@ -1653,6 +1654,7 @@ msgstr "Expression" #: src/pages/policies/expression/ExpressionPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Expression using Python." @@ -2316,6 +2318,7 @@ msgstr "Loading" #: src/pages/applications/ApplicationForm.ts #: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts +#: src/pages/events/TransportForm.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/groups/GroupForm.ts @@ -2564,6 +2567,7 @@ msgstr "My Applications" #: src/pages/policies/reputation/ReputationPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts #: src/pages/property-mappings/PropertyMappingListPage.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts #: src/pages/providers/ProviderListPage.ts @@ -3530,6 +3534,7 @@ msgstr "Secret:" #: src/pages/policies/expression/ExpressionPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "See documentation for a list of all variables." @@ -3968,6 +3973,7 @@ msgid "Successfully created invitation." msgstr "Successfully created invitation." #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Successfully created mapping." @@ -4124,6 +4130,7 @@ msgid "Successfully updated invitation." msgstr "Successfully updated invitation." #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Successfully updated mapping." @@ -5071,6 +5078,10 @@ msgstr "Webhook (Slack/Discord)" msgid "Webhook (generic)" msgstr "Webhook (generic)" +#: src/pages/events/TransportForm.ts +msgid "Webhook Mapping" +msgstr "Webhook Mapping" + #: src/pages/events/TransportForm.ts msgid "Webhook URL" msgstr "Webhook URL" diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index 2eb7de7b55..8de31ae499 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -1638,6 +1638,7 @@ msgstr "" #: src/pages/events/EventInfo.ts #: src/pages/policies/expression/ExpressionPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Expression" @@ -1645,6 +1646,7 @@ msgstr "" #: src/pages/policies/expression/ExpressionPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Expression using Python." @@ -2308,6 +2310,7 @@ msgstr "" #: src/pages/applications/ApplicationForm.ts #: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts +#: src/pages/events/TransportForm.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/groups/GroupForm.ts @@ -2556,6 +2559,7 @@ msgstr "" #: src/pages/policies/reputation/ReputationPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts #: src/pages/property-mappings/PropertyMappingListPage.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts #: src/pages/providers/ProviderListPage.ts @@ -3522,6 +3526,7 @@ msgstr "" #: src/pages/policies/expression/ExpressionPolicyForm.ts #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "See documentation for a list of all variables." @@ -3960,6 +3965,7 @@ msgid "Successfully created invitation." msgstr "" #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Successfully created mapping." @@ -4116,6 +4122,7 @@ msgid "Successfully updated invitation." msgstr "" #: src/pages/property-mappings/PropertyMappingLDAPForm.ts +#: src/pages/property-mappings/PropertyMappingNotification.ts #: src/pages/property-mappings/PropertyMappingSAMLForm.ts #: src/pages/property-mappings/PropertyMappingScopeForm.ts msgid "Successfully updated mapping." @@ -5056,6 +5063,10 @@ msgstr "" msgid "Webhook (generic)" msgstr "" +#: src/pages/events/TransportForm.ts +msgid "Webhook Mapping" +msgstr "" + #: src/pages/events/TransportForm.ts msgid "Webhook URL" msgstr "" diff --git a/web/src/pages/events/TransportForm.ts b/web/src/pages/events/TransportForm.ts index 6be637f253..42d0abb59b 100644 --- a/web/src/pages/events/TransportForm.ts +++ b/web/src/pages/events/TransportForm.ts @@ -1,4 +1,9 @@ -import { EventsApi, NotificationTransport, NotificationTransportModeEnum } from "@goauthentik/api"; +import { + EventsApi, + NotificationTransport, + NotificationTransportModeEnum, + PropertymappingsApi, +} from "@goauthentik/api"; import { t } from "@lingui/macro"; import { customElement, property } from "lit-element"; import { html, TemplateResult } from "lit-html"; @@ -7,6 +12,7 @@ import { ifDefined } from "lit-html/directives/if-defined"; import "../../elements/forms/HorizontalFormElement"; import { first } from "../../utils"; import { ModelForm } from "../../elements/forms/ModelForm"; +import { until } from "lit-html/directives/until"; @customElement("ak-event-transport-form") export class TransportForm extends ModelForm { @@ -112,6 +118,32 @@ export class TransportForm extends ModelForm { class="pf-c-form-control" /> + + +
{ + loadInstance(pk: string): Promise { + return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsNotificationRetrieveRaw({ + pmUuid: pk, + }); + } + + getSuccessMessage(): string { + if (this.instance) { + return t`Successfully updated mapping.`; + } else { + return t`Successfully created mapping.`; + } + } + + send = (data: NotificationWebhookMapping): Promise => { + if (this.instance) { + return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsNotificationUpdate({ + pmUuid: this.instance.pk || "", + notificationWebhookMappingRequest: data, + }); + } else { + return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsNotificationCreate({ + notificationWebhookMappingRequest: data, + }); + } + }; + + renderForm(): TemplateResult { + return html`
+ + + + + + +

+ ${t`Expression using Python.`} + + ${t`See documentation for a list of all variables.`} + +

+ +
`; + } +} diff --git a/website/docs/events/transports.md b/website/docs/events/transports.md index b77fadf3a0..a472456944 100644 --- a/website/docs/events/transports.md +++ b/website/docs/events/transports.md @@ -19,6 +19,14 @@ This will send a POST request to the given URL with the following contents: The `Content-Type` header is set to `text/json`. +Starting in 2021.10, you can also select a Notification mapping. This allows you to freely configure the request's payload. For example: + +```python +return { + "foo": context['notification'].body, +} +``` + ## Slack Webhook This sends a request using the Slack-specific format. This is also compatible with Discord's webhooks by appending `/slack` to the Discord webhook URL.