Create an action that sends a Slack notification about a validation.
send_slack_notification(
webhook_url=None,
step_msg=None,
summary_msg=None,
debug=False,
)
Slack is a common place for a team to hear about problems with its data. The send_slack_notification() function creates an action that posts a message to a Slack channel through an incoming webhook. The same action works in two places:
- In
Actions, it sends a message about a step that exceeded a threshold level.
- In
FinalActions, it sends a summary of the whole validation after the interrogation.
The action works out from where it runs which kind of message to send. The messages are made from templates (step_msg= and summary_msg=), which can use Slack’s Markdown formatting.
A notification that can’t be sent (because of a wrong webhook URL, a network error or a template with an unknown variable) doesn’t stop the validation. The error is printed as Failed to send Slack notification: ... instead of being raised.
Without a webhook URL, nothing is sent to Slack, which is useful for trying out templates:
- With
webhook_url=None, the action prints the message that it would send.
- With
webhook_url=None and debug=True, the function prints previews of both templates filled in with sample values. It then returns None instead of an action.
Parameters
webhook_url: str | None = None
-
The URL of a Slack incoming webhook (https://hooks.slack.com/services/...). Since anyone with the URL can post to the channel, it’s best read from an environment variable rather than written in the code. The default is None, which prints the messages instead of sending them.
step_msg: str | None = None
-
The template for messages about a step, using the variables in Template Variables for Step Messages below. The default is None, which uses a template with the assertion type, the threshold level, the step number, the column and the time.
summary_msg: str | None = None
-
The template for summary messages, using the variables in Template Variables for Summary Messages below. The default is None, which uses a template with the highest threshold level reached, the number of steps, the number of failing steps and the time.
debug: bool = False
-
Whether to print each message, and Slack’s response, when it’s sent. With
webhook_url=None, the function prints previews of the templates instead (see above). The default is False.
Returns
Callable or None
-
The action, to use in Actions or FinalActions. With
webhook_url=None and debug=True, the function returns None after printing the previews.
Template Variables For Step Messages
A step_msg= template can use these variables:
"{step}": The step number.
"{column}": The column name.
"{value}": The value being compared (only available in certain validation steps).
"{type}": The assertion type (e.g., "col_vals_gt", etc.).
"{level}": The threshold level ("warning", "error", or "critical").
"{level_num}": The threshold level as a number (30, 40, or 50).
"{autobrief}": A localized and brief statement of the expectation for the step.
"{failure_text}": Localized text that explains how the validation step failed.
"{time}": The time of the notification.
These are the keys of get_action_metadata()’s dictionary, plus "{time}".
Template Variables For Summary Messages
A summary_msg= template can use these variables:
"{n_steps}": The total number of validation steps.
"{n_passing_steps}": The number of validation steps where all test units passed.
"{n_failing_steps}": The number of validation steps that had some failing test units.
"{n_warning_steps}": The number of steps that exceeded a ‘warning’ threshold.
"{n_error_steps}": The number of steps that exceeded an ‘error’ threshold.
"{n_critical_steps}": The number of steps that exceeded a ‘critical’ threshold.
"{all_passed}": Whether or not every validation step had no failing test units.
"{highest_severity}": The highest threshold level reached in the validation: "warning", "error" or "critical". If no threshold was exceeded, it is "some failing" when some test units failed and "all passed" otherwise.
"{tbl_row_count}": The number of rows in the target table.
"{tbl_column_count}": The number of columns in the target table.
"{tbl_name}": The name of the target table ("Unknown" without a tbl_name=).
"{validation_duration}": The duration of the validation in seconds.
"{time}": The time of the notification.
These are keys of get_validation_summary()’s dictionary, plus "{time}". Its other keys, such as "{overall_health_score}" and "{list_failing_steps}", can be used too. Since the templates are filled in with Python’s str.format(), a literal brace has to be doubled ("{{" or "}}").
Examples
Let’s validate a small table of orders, which has a missing customer name and two amounts that aren’t positive. The action has no webhook URL, so it prints the message instead of sending it. In Actions(critical=), it runs for the steps that exceed the critical threshold:
import pointblank as pb
import polars as pl
orders = pl.DataFrame(
{
"amount": [29.99, 150.00, -5.00, 75.50, 0.00],
"customer": ["Alice", "Bob", None, "Dana", "Eve"],
}
)
notify_slack = pb.send_slack_notification()
validation = (
pb.Validate(
data=orders,
tbl_name="orders",
thresholds=(0.05, 0.20, 0.40),
actions=pb.Actions(critical=notify_slack),
)
.col_vals_not_null(columns="customer")
.col_vals_gt(columns="amount", value=0)
.interrogate()
)
=== Dry Run ===
Would send to Slack:
*Data Validation Alert*
• Type: col_vals_gt
• Level: critical
• Step: 2
• Column: amount
• Time: 2026-11-03 23:22:06.080144
==============
Only the second step, with 40% of its test units failing, reached the critical threshold, so there is one message. The first step (20% failing) exceeded the error threshold, which has no action here.
In FinalActions, the same action sends a summary of the validation instead:
validation = (
pb.Validate(
data=orders,
tbl_name="orders",
thresholds=(0.05, 0.20, 0.40),
final_actions=pb.FinalActions(notify_slack),
)
.col_vals_not_null(columns="customer")
.col_vals_gt(columns="amount", value=0)
.interrogate()
)
=== Dry Run ===
Would send to Slack:
*Data Validation Summary*
• Highest Severity: critical
• Total Steps: 2
• Failed Steps: 2
• Time: 2026-11-03 23:22:06.094717
==============
The summary is sent once per interrogation, whether or not any step failed. Both steps failed here, and the highest threshold level reached was critical.
Custom templates can add more detail. With webhook_url=None and debug=True, the function shows previews of the templates with sample values, without a validation:
pb.send_slack_notification(
step_msg=(
"🚨 *Step {step} ({type})* exceeded the {level} threshold\n"
"• Column: {column}\n"
"• Details: {failure_text}"
),
summary_msg=(
"📊 *Validation of {tbl_name}*: {highest_severity}\n"
"• Failing steps: {n_failing_steps} of {n_steps}\n"
"• Rows: {tbl_row_count}"
),
debug=True,
)
=== Step Notification Preview ===
🚨 *Step 1 (col_vals_gt)* exceeded the critical threshold
• Column: column_name
• Details: 25% of values failed this test
=== Summary Notification Preview ===
📊 *Validation of example_table*: critical
• Failing steps: 2 of 5
• Rows: 1000
The sample values show where each variable goes. For a real channel, we pass the webhook URL and use the action for both kinds of message. With Actions(default=), a step message is sent for every step that exceeds a threshold, at its highest level:
import os
notify_slack = pb.send_slack_notification(webhook_url=os.environ["SLACK_WEBHOOK_URL"])
validation = (
pb.Validate(
data=orders,
tbl_name="orders",
thresholds=(0.05, 0.20, 0.40),
actions=pb.Actions(default=notify_slack),
final_actions=pb.FinalActions(notify_slack),
)
.col_vals_not_null(columns="customer")
.col_vals_gt(columns="amount", value=0)
.interrogate()
)
This interrogation posts three messages to the channel: one for each failing step (at the error and critical levels), then the summary. Reading the URL from the SLACK_WEBHOOK_URL environment variable keeps it out of the code.