Webhooks

Webhook actions in Django Action Triggers allow you to send a request to a specified URL whenever a trigger is activated. This can be useful for integrating your Django application with external services, such as notifying other systems when certain events occur.

Creating a Webhook Action

To create a webhook action, you need to follow these steps:

  1. Create a :class:`Config` model instance (defines the base action).

  2. Create a :class:`Webhook` model instance (defines the webhook-specific action).

  3. Create a :class:`ConfigSignal` model instance (defines the trigger).

Scenario

Suppose you have the following Django models:

from django.db import models

class Customer(models.Model):
    name = models.CharField(max_length=255)
    email = models.EmailField()

class Product(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()


class Sale(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.IntegerField()

Now , suppose you want to trigger a webhook whenever a new sale is created. Here’s how you can set that up:

Step 1: Whitelist the Content Types and Endpoint Patterns

Before you can get started with webhooks, you must whitelist the content types and endpoint patterns in the whitelisted_content_types and whitelisted_webhook_endpoint_patterns settings in your Django project’s settings.py file.

Visit the Action Trigger Configuration Options guide for more information on whitelisting content types and endpoint patterns.

Step 2: Create a Config Model Instance

The Config model forms the basis of any action. It defines the payload that will be sent when the action is triggered. The payload can use Django template syntax to include dynamic data.

from django.contrib.contenttypes.models import ContentType
from action_triggers.models import Config

config = Config.objects.create(
    payload={
        "customer_name": "{{ customer.name }}",
        "product_name": "{{ product.name }}",
        "quantity": "{{ quantity }}"
    },
    active=True,
    content_types=[
        ContentType.objects.get_for_model(Sale)
    ]
)

Step 3: Create a Webhook Model Instance

The Webhook model defines the URL and method used to send the request when the trigger is activated.

Warning

In the example below, the API key is hardcoded. This is not recommended because storing sensitive information in plaintext in the database is insecure. Instead, use a callable to fetch the API key at runtime (explained in the next section).

from action_triggers.models import Webhook
from action-triggers.enums import HTTPMethod

webhook = Webhook.objects.create(
  config=config,
  url="https://example.com/webhook",
  method=HTTPMethod.POST,
  headers={
    "Content-Type": "application/json",
    "Authorization": "Bearer my-api-key"
  },
  timeout_secs=10.0
)

Step 4: Create a ConfigSignal Model Instance

ally, the ConfigSignal model links the action to a specific trigger event, such as saving a model instance.

from action_triggers.models import ConfigSignal
from action_triggers.enums import SignalChoices

config_signal = ConfigSignal.objects.create(
    config=config,
    signal=SignalChoices.POST_SAVE,
)

Now, whenever a new sale is created (or updated, if using POST_SAVE), the webhook will be triggered.

Dynamically Setting Headers

In the previous example, the API key was hardcoded in the webhooks.headers field. This is insecure because the key is stored in plaintext. Instead, you can dynamically set the header values at runtime by using a callable.

Replacing Hardcoded Headers

Suppose you have a function myproject.my_module.fetch_api_key that retrieves the API key securely. You can specify the path to this function in the webhooks.headers field:

from action_triggers.models import Webhook
from action-triggers.enums import HTTPMethod

webhook = Webhook.objects.create(
    url="https://example.com/webhook",
    method=HTTPMethod.POST,
    config=config,
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer {{ myproject.my_module.fetch_api_key }}"
    }
)

Adding Dynamic Import Paths to Settings

To use dynamic imports for headers (or any other fields), you must allow the specific callable or variable in your settings.

Add the following to your settings.py file:

ACTION_TRIGGER_SETTINGS = {
    'ALLOWED_DYNAMIC_IMPORT_PATHS': (
        'myproject.my_module.fetch_api_key',
    ),
}

This configuration ensures that the specified callable can be safely evaluated at runtime.

For more information on dynamically setting headers, refer to the Dynamically Loading Values at Runtime guide.

Best Practices

  • Avoid Hardcoding Sensitive Information: Use dynamic imports to manage sensitive information such as API keys.

  • Test Your Webhooks: Ensure that your webhook is functioning correctly by testing it with different scenarios.

  • Monitor Webhook Responses: Keep track of webhook responses to ensure that your external systems are receiving and processing the requests correctly.

  • Set Timeout Limits: Define a maximum timeout for webhooks to prevent long-running requests from blocking your application. This can be done by setting ACTION_TRIGGER_SETTINGS.MAX_WEBHOOK_TIMEOUT in your settings.

By following these steps and best practices, you can effectively integrate webhooks into your Django project using Django Action Triggers. For more advanced configurations, refer to other sections of this documentation.