Webhook with custom Transformation
With a custom webhook, Infrahub runs a Python Transformation on event data before it sends the result to an external endpoint. Choose a custom webhook when the receiving system requires a specific payload format, such as Slack, Microsoft Teams, GitHub Actions, or other third-party APIs.
For general information about Python Transformations, see the Python Transformation guide. For details about webhook events and payloads, see Webhooks.
When using a Transformation with a webhook, the Transformation receives the webhook event data directly instead of executing its GraphQL query. The query attribute is still required on the Transformation class but is not used. See GitHub issue #6650 for updates on this requirement.
Create the Python Transformation​
Create a Python file with a Transformation class that processes webhook event data. The Transformation receives a dictionary containing the event information:
from typing import Any
from infrahub_sdk.transforms import InfrahubTransform
class SlackWebhookTransform(InfrahubTransform):
query = "placeholder_query" # Required but not used for webhooks
timeout = 10
async def transform(self, data: dict[str, Any]) -> dict[str, Any]:
event_type = data.get("event", "unknown")
node_data = data.get("data", {})
node_kind = node_data.get("kind", "Unknown")
action = node_data.get("action", "unknown")
# Format payload for Slack incoming webhook
return {
"text": f"Infrahub: {node_kind} {action}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*Event:* `{event_type}`\n*Kind:* {node_kind}\n*Branch:* {data.get('branch') or 'default'}"
}
}
]
}
The data parameter contains:
| Field | Description |
|---|---|
data | Original event payload (node changes, changelog, etc.) |
id | Unique event identifier (UUID) |
event | Event type, such as infrahub.node.updated |
branch | Branch name or None for branch-independent events |
account_id | UUID of the account that triggered the event |
occured_at | ISO 8601 timestamp of when the event occurred |
See Webhooks for example event payloads.
Create a placeholder GraphQL query​
Create a GraphQL query file. This query is required but not executed for webhook Transformations:
query PlaceholderQuery {
InfrahubStatus {
summary {
active_schema_hash
}
}
}
Configure .infrahub.yml​
Add the Transformation and query to your repository configuration:
# yaml-language-server: $schema=https://schema.infrahub.app/python-sdk/repository-config/latest.json
---
python_transforms:
- name: slack_webhook_transform
class_name: SlackWebhookTransform
file_path: "transforms/slack_webhook.py"
queries:
- name: placeholder_query
file_path: "queries/placeholder.gql"
Add the repository to Infrahub​
Add your repository containing the Transformation to Infrahub. See Connect a repository for instructions.
Create the custom webhook​
Create the webhook while working on the default branch. A custom webhook uses only the Transformation linked on the default branch, so the Transformation has to exist there before you link it. See Where webhooks are stored and read for the reason.
- Via the Web Interface
- Via the GraphQL Interface
- Navigate to Integrations > Webhooks.
- Click + Add Webhook.
- Select Custom Webhook as the type.
- Fill in the webhook details:
- Name: A descriptive name, such as "Slack Notifications"
- URL: The destination endpoint
- Event Type: The event to trigger on, such as
infrahub.node.updated - Branch Scope: Which branches should trigger this webhook
- Select your Transformation from the Transformation dropdown. If it is not in the list, it does not exist on the default branch yet.
- Click Save.
First, get the Transformation ID. Run the query without selecting a branch, so that it queries the default branch. If it returns no result, the Transformation does not exist on the default branch yet.
query GetTransform {
CoreTransformPython(name__value: "slack_webhook_transform") {
edges {
node {
id
}
}
}
}
Then create the custom webhook:
mutation CreateCustomWebhook {
CoreCustomWebhookCreate(
data: {
name: {value: "Slack Notifications"}
description: {value: "Send node change notifications to Slack"}
url: {value: "https://hooks.slack.com/services/XXX/YYY/ZZZ"}
event_type: {value: "infrahub.node.updated"}
branch_scope: {value: "default_branch"}
transformation: {id: "<transform-id-from-query>"}
}
) {
object {
id
display_label
}
}
}
Which Transformation code runs for events on other branches​
When the branch scope of the webhook includes other branches, Infrahub still reads the link to the Transformation from the default branch. Which version of the Transformation code it runs depends on the repository type:
- With a Repository (read-write), Infrahub runs the code from the commit of the branch the event was emitted on. You can change and test the Transformation code on a branch, as long as the link to the Transformation exists on the default branch.
- With a Read-only Repository, Infrahub always runs the code from the latest commit of the repository's configured ref.
With a Repository (read-write), Infrahub sends nothing for an event on a data-only branch (sync_with_git: false), because the repository has no Git branch with that name. The failure does not appear in the related tasks of the webhook. It is recorded only in the logs of the task worker.
Verify the webhook​
- Make a change that triggers the configured event. For example, update a node if using
infrahub.node.updated. - Check that your external endpoint received the transformed payload.
If the receiver gets the standard event payload instead of your transformed payload, the webhook has no Transformation linked on the default branch. Switch to the default branch, open the webhook, and set its Transformation field.
If a delivery does not arrive, open the webhook and select the delivery from its related tasks. Its logs show the request that was sent and the delivery outcome, and on failure the classified reason. See Header and payload logging for what each delivery records.
If the event matches the branch scope and node kind of the webhook but the webhook has no delivery for it, Infrahub failed before it created the delivery, for example while it ran the Transformation. The error is recorded only in the task worker logs.