- Overview
- API Resources
Webhook event reference
Request headers, CloudEvents body, rule outcomes, size limits, retry schedule, and endpoint requirements for the data quality webhook channel.
This page specifies what an endpoint registered as a webhook alert channel receives: the HTTP request, the event body, how rule results map to outcomes, the limits applied to large runs, and how deliveries are retried.
Request
Each delivery is a POST with a JSON body. Header names are case-insensitive and are sent in lowercase.
POST /hooks/peak HTTP/1.1
content-type: application/json
user-agent: Peak-Webhooks/1.0
webhook-id: evt_1f0c6b2a9d3e4c5f8a7b6c5d4e3f2a1b
webhook-timestamp: 1791191520
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
x-peak-event-type: ai.peak.data_validation.run.completed
x-env: prod
POST /hooks/peak HTTP/1.1
content-type: application/json
user-agent: Peak-Webhooks/1.0
webhook-id: evt_1f0c6b2a9d3e4c5f8a7b6c5d4e3f2a1b
webhook-timestamp: 1791191520
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
x-peak-event-type: ai.peak.data_validation.run.completed
x-env: prod
| Header | Sent | Value |
|---|---|---|
content-type | Always | application/json |
user-agent | Always | Peak-Webhooks/1.0 |
webhook-id | Always | The event id. Identical on every retry of the same event, so it is the key to deduplicate on. |
webhook-timestamp | Always | Unix time in seconds at which this attempt was sent. Fresh on every retry. |
webhook-signature | auth.type is hmac | v1, followed by the base64 HMAC-SHA256 of <webhook-id>.<webhook-timestamp>.<body>. Recomputed on every retry. See Verify the signature. |
x-peak-event-type | Always | The event type, for routing before the body is parsed. |
authorization | auth.type is bearer-token or basic | Bearer <token>, or Basic <base64 of username:password>. |
<headerName> | auth.type is api-key | The key, in the header named by auth.headerName (default X-API-Key). |
| Static headers | When the channel defines headers | The configured names and values, as given. |
Event body
The body is a CloudEvents 1.0 envelope in structured JSON mode. One event is sent per validation run per channel, carrying the rules of that run which name the channel, narrowed to the outcomes the channel subscribes to.
In this example three rules on stage.orders name the channel, which subscribes to every outcome. The freshness check failed, the missing-data check produced a warning, and the foreign-key check passed:
{
"specversion": "1.0",
"id": "evt_1f0c6b2a9d3e4c5f8a7b6c5d4e3f2a1b",
"type": "ai.peak.data_validation.run.completed",
"source": "/peak/prod/data-validation/acme",
"subject": "orders-pipeline/stage.orders",
"time": "2026-10-05T09:12:00.123Z",
"datacontenttype": "application/json",
"data": {
"tenant": { "name": "acme", "id": "1234" },
"run": { "id": "e3f1c2a4-9b77-4d21-8c5e-1a2b3c4d5e6f" },
"object": {
"solution": "orders-pipeline",
"table": "stage.orders",
"warehouseType": "snowflake"
},
"outcome": "failed",
"matched": [
"ai.peak.data_validation.rule.failed",
"ai.peak.data_validation.rule.passed",
"ai.peak.data_validation.rule.warned"
],
"summary": { "total": 3, "errored": 0, "failed": 1, "warned": 1, "passed": 1, "skipped": 0 },
"results": [
{
"rule": {
"type": "freshness",
"severity": "error",
"column": "order_date",
"params": { "column": "order_date", "threshold": 24, "unit": "hours" }
},
"outcome": "failed",
"status": "failed",
"message": "Data is stale: column 'order_date' is 50 hours old (threshold: 24 hours)",
"observed": 50,
"errorCodes": ["DI_E_24F01"],
"details": {
"last_seen": "2026-10-03 07:12:44.000",
"threshold": { "value": 24, "unit": "hours" }
}
},
{
"rule": {
"type": "missing_data_validation",
"severity": "warn",
"column": "order_date",
"params": { "column": "order_date", "interval": 1, "unit": "days", "minRowCount": 100 }
},
"outcome": "warned",
"status": "failed",
"message": "Missing data: 2 period(s) of 1 days had fewer than 100 rows",
"observed": 2,
"errorCodes": ["DI_W_24M01"],
"details": {
"expected_min": 100,
"slice": { "interval": 1, "unit": "days" },
"window": { "start": "2026-09-01 00:00:00.000", "end": "2026-10-05 09:11:58.410" },
"missing_periods": [
{ "start": "2026-10-03 00:00:00.000", "end": "2026-10-04 00:00:00.000", "actual_count": 41 },
{ "start": "2026-10-04 00:00:00.000", "end": "2026-10-05 00:00:00.000", "actual_count": 0 }
]
}
},
{
"rule": { "type": "async_foreign_key", "severity": "warn" },
"outcome": "passed",
"status": "passed"
}
],
"resultsTotal": 3,
"resultsTruncated": false,
"links": {
"dashboard": "https://platform.peak.ai/canvas/assets",
"run": "https://service.peak.ai/validation-api/api/v1/validations/run/e3f1c2a4-9b77-4d21-8c5e-1a2b3c4d5e6f",
"docs": "https://docs.uipath.com/es/industry-department-solutions/automation-cloud/latest/supply-chain-retail-api-guide/webhook-event-reference"
}
}
}
{
"specversion": "1.0",
"id": "evt_1f0c6b2a9d3e4c5f8a7b6c5d4e3f2a1b",
"type": "ai.peak.data_validation.run.completed",
"source": "/peak/prod/data-validation/acme",
"subject": "orders-pipeline/stage.orders",
"time": "2026-10-05T09:12:00.123Z",
"datacontenttype": "application/json",
"data": {
"tenant": { "name": "acme", "id": "1234" },
"run": { "id": "e3f1c2a4-9b77-4d21-8c5e-1a2b3c4d5e6f" },
"object": {
"solution": "orders-pipeline",
"table": "stage.orders",
"warehouseType": "snowflake"
},
"outcome": "failed",
"matched": [
"ai.peak.data_validation.rule.failed",
"ai.peak.data_validation.rule.passed",
"ai.peak.data_validation.rule.warned"
],
"summary": { "total": 3, "errored": 0, "failed": 1, "warned": 1, "passed": 1, "skipped": 0 },
"results": [
{
"rule": {
"type": "freshness",
"severity": "error",
"column": "order_date",
"params": { "column": "order_date", "threshold": 24, "unit": "hours" }
},
"outcome": "failed",
"status": "failed",
"message": "Data is stale: column 'order_date' is 50 hours old (threshold: 24 hours)",
"observed": 50,
"errorCodes": ["DI_E_24F01"],
"details": {
"last_seen": "2026-10-03 07:12:44.000",
"threshold": { "value": 24, "unit": "hours" }
}
},
{
"rule": {
"type": "missing_data_validation",
"severity": "warn",
"column": "order_date",
"params": { "column": "order_date", "interval": 1, "unit": "days", "minRowCount": 100 }
},
"outcome": "warned",
"status": "failed",
"message": "Missing data: 2 period(s) of 1 days had fewer than 100 rows",
"observed": 2,
"errorCodes": ["DI_W_24M01"],
"details": {
"expected_min": 100,
"slice": { "interval": 1, "unit": "days" },
"window": { "start": "2026-09-01 00:00:00.000", "end": "2026-10-05 09:11:58.410" },
"missing_periods": [
{ "start": "2026-10-03 00:00:00.000", "end": "2026-10-04 00:00:00.000", "actual_count": 41 },
{ "start": "2026-10-04 00:00:00.000", "end": "2026-10-05 00:00:00.000", "actual_count": 0 }
]
}
},
{
"rule": { "type": "async_foreign_key", "severity": "warn" },
"outcome": "passed",
"status": "passed"
}
],
"resultsTotal": 3,
"resultsTruncated": false,
"links": {
"dashboard": "https://platform.peak.ai/canvas/assets",
"run": "https://service.peak.ai/validation-api/api/v1/validations/run/e3f1c2a4-9b77-4d21-8c5e-1a2b3c4d5e6f",
"docs": "https://docs.uipath.com/es/industry-department-solutions/automation-cloud/latest/supply-chain-retail-api-guide/webhook-event-reference"
}
}
}
Envelope
| Field | Description |
|---|---|
specversion | Always 1.0. |
id | Unique per event: evt_ followed by 32 hexadecimal characters. Equals the webhook-id header. Two channels notified for the same run receive different ids. |
type | Always ai.peak.data_validation.run.completed. What happened is in data.outcome and data.summary. |
source | /peak/<environment>/data-validation/<tenant>. <environment> is prod for production, so an endpoint fed by more than one environment can tell them apart. |
subject | <solution>/<table>. |
time | When the run completed, in RFC 3339 format, UTC, with millisecond precision. |
datacontenttype | Always application/json. |
data
| Field | Description |
|---|---|
tenant.name, tenant.id | The tenant the run belongs to. id is a string. |
run.id | The validation run. It is the same in every event sent for that run, whichever channel receives it, so it correlates them. The full report is available from Check a validation run. |
object.solution, object.table, object.warehouseType | The table that was validated and its warehouse, snowflake or redshift. |
outcome | The worst outcome among the carried results, in the order errored, failed, warned, passed. |
matched | The channel's subscribed outcomes that are present in the carried results, as ai.peak.data_validation.rule.<outcome> names, sorted. This is why the channel was notified. |
summary | Counts of the carried results by outcome: total, errored, failed, warned, passed, skipped. Complete even when results is capped. Skipped rules are never carried, so skipped is 0. |
results | One entry per carried result, ordered errored, failed, warned, passed. Capped, see Size limits. |
resultsTotal, resultsTruncated | The number of carried results, and whether results holds fewer than that. |
links.dashboard, links.run, links.docs | The Canvas asset list where the Data Quality Dashboard lives, the run report endpoint, and this documentation. For a spoke tenant, links.run is on the spoke's service host. |
Result entry
| Field | Present | Description |
|---|---|---|
rule.type | Always | freshness, missing_data_validation, or async_foreign_key. |
rule.severity | Always | error or warn, from the validation entry. |
rule.column | When the rule measures a column | The column, for freshness and missing_data_validation. |
rule.params | When the rule has parameters | The parameters the rule was evaluated with. |
outcome | Always | failed, warned, errored, or passed. See Rule outcomes. |
status | Always | The raw run status: passed, failed, or error. |
message | Problems only | The human-readable result, the same text a Slack or email alert shows. |
observed | Problems, when measured | The measurement: the age of the newest record for freshness, the number of short periods for missing_data_validation, the number of orphan rows for async_foreign_key. |
errorCodes | Problems only | The DI_E_ or DI_W_ codes of the result. See Results. |
details | Problems, when available | Rule-specific evidence: last_seen and threshold for freshness; expected_min, slice, window, and missing_periods for missing data; constraints and totalOrphanRows for foreign keys. Lists are sampled, see Size limits. |
detailsOmitted | When details was dropped for size | true when a rule's details exceeded 2 KB and were left out. |
Passing results carry only rule, outcome, and status, so a receiver sees what was checked without the evidence a problem needs.
Rule outcomes
A result's outcome is its status read through its severity. The outcome names, prefixed ai.peak.data_validation.rule., are the values a channel's events list accepts.
status | severity | Outcome | Subscription value |
|---|---|---|---|
failed | error | failed | ai.peak.data_validation.rule.failed |
failed | warn | warned | ai.peak.data_validation.rule.warned |
error | any | errored | ai.peak.data_validation.rule.errored |
passed | any | passed | ai.peak.data_validation.rule.passed |
skipped | any | skipped | None. Skipped rules are never delivered. |
A channel's event carries the rules that name the channel and whose outcome is in its events list, which defaults to all four for a webhook. A run in which none of the carried rules has a listed outcome produces no event. A run whose carried rules all passed has outcome: passed and reaches only channels subscribed to ai.peak.data_validation.rule.passed.
Size limits
| Limit | Behavior |
|---|---|
| Results per event | At most 100. Beyond that, results holds the first 100 in outcome order, resultsTotal has the full count, and resultsTruncated is true. |
Lists inside details (missing_periods, missing_dates) | Longer than 20 items, the last 20 are kept, with <key>_total and <key>_truncated: true alongside. |
details per result | Larger than 2 KB after sampling, the details are replaced by detailsOmitted: true. |
| Whole body | About 60 KB. If a body is larger, details is dropped from every result; if it is still too large, results is halved repeatedly, down to none. summary and resultsTotal stay complete. |
Delivery and retries
An event is handed to Peak's delivery service when the run completes. At that point the run report lists the channel in alerts_sent, which records that the event was accepted for delivery, not that your endpoint received it.
| Aspect | Behavior |
|---|---|
| Request | POST, with the body serialised once. The endpoint must respond within 10 seconds (5 seconds for the verification test event). The response body is ignored. |
| Success | Any 2xx status. |
| Redirects | Never followed. A 3xx is a permanent failure. |
| Retried | 408, 425, 429, any 5xx, a timeout, and connection or TLS failures. Six attempts in total, the retries after 10 seconds, 1 minute, 5 minutes, 30 minutes, and 2 hours. A Retry-After header on a 429 or 5xx response lengthens the wait, within 5 seconds to 1 hour. After the sixth failed attempt the event is dropped. |
| Not retried | 3xx, 410, 401 or 403 when a credential was sent, any other 4xx, a hostname that does not resolve, a URL that fails the endpoint requirements, and a credential that is missing, expired, or in the wrong format. Fix the cause; the next run delivers normally. |
| Retries | Carry the same webhook-id and body, with a new webhook-timestamp and signature. Delivery is at-least-once, so deduplicate on webhook-id. |
| Order | Not guaranteed. A retried older event can arrive after a newer one; time and data.run.id tell them apart. |
| Source addresses | Deliveries originate from the Peak platform. If your endpoint restricts callers by IP address, contact support for the addresses to allow. |
Endpoint requirements
https, on port 443. An explicit:443is accepted.- A public, fully qualified hostname. IP addresses,
localhost, single-label names, and names ending in.local,.localhost,.internal,.svc, or.cluster.localare rejected when the channel is registered. - Every address the hostname resolves to must be public. A name that resolves to a private, loopback, or link-local address is refused at send time with the reason
egress_blocked, and the check is repeated on every delivery. - TLS 1.2 or later, with a certificate issued by a public certificate authority. A self-signed certificate fails as a
networkerror. - No embedded credentials and no fragment in the URL. A query string is allowed.
- A
2xxresponse within the timeout. Accept the event, then process it; a slow handler is retried as atimeout, and the retries repeat the same event.
Verification test event
Registering, updating, or verifying a channel sends one ai.peak.webhook.ping event to the URL, with the same headers and signature as a delivery and x-peak-event-type: ai.peak.webhook.ping. The channel is stored only if the endpoint answers 2xx within 5 seconds.
{
"specversion": "1.0",
"id": "evt_7c1b9e7a-5d2f-4a1e-9b3c-2f6d8e0a4c11",
"type": "ai.peak.webhook.ping",
"source": "/peak/prod/notifications/acme",
"subject": "alerts.example.com",
"time": "2026-10-05T09:11:58.410Z",
"datacontenttype": "application/json",
"data": {
"message": "Test event sent to verify this webhook destination. No action is required.",
"tenant": { "id": "1234", "name": "acme" }
}
}
{
"specversion": "1.0",
"id": "evt_7c1b9e7a-5d2f-4a1e-9b3c-2f6d8e0a4c11",
"type": "ai.peak.webhook.ping",
"source": "/peak/prod/notifications/acme",
"subject": "alerts.example.com",
"time": "2026-10-05T09:11:58.410Z",
"datacontenttype": "application/json",
"data": {
"message": "Test event sent to verify this webhook destination. No action is required.",
"tenant": { "id": "1234", "name": "acme" }
}
}
Compatibility
Fields are only ever added within an event type, so a receiver must ignore fields and enumeration values it does not recognise. id, type, source, subject, data.run.id, data.outcome, and data.summary keep their meaning. A breaking change ships as a new type.