Acceldata
Acceldata
Last updated:

Working With Notification Templates

Accessing Notification Templates

  1. In ADOC, navigate to the left main navigation menu, click Control Center -> Settings.
  2. From the Settings page, select Notification Templates under Notifications.

You’ll see a table of existing templates and template groups (if configured).

Creating a Notification Template

Create the Template Group

  1. On the Notification page, click Create Notification Template.

  2. Enter:

    1. Name: Use something descriptive (e.g., Prod – DQ – Slack & PagerDuty)
    2. Description (Optional): Include purpose and audience, e.g., “On-call alerts for prod DQ policies”
  3. Click Save.

You’ll be redirected to the Edit Notification Template page for this new group.

Customizing Templates by Source and Channel

Templates in a group are defined per source type and per channel.

Select Source Type and Channel

In the Edit Notification Template page:

  1. From the left panel, choose a Source Type (for example, Data Quality, Pipeline Monitoring, Profile).
  2. Select a Channel:
    1. Email
    2. Webhook
    3. Slack
    4. Google Chat
    5. Microsoft Teams
    6. ServiceNow
    7. Jira

ADOC loads the system template for that combination as a starting point.

Edit Subject / Content

For each channel:

ChannelWhat You Edit
EmailSubject and Body (HTML or plain text)
WebhookJSON payload content
SlackBlock Kit JSON body
Google ChatCard JSON body
Microsoft TeamsMessageCard / Adaptive Card JSON
ServiceNowPlain text body or JSON fields
JiraJSON payload (issue fields / ADF document)

You write the content in FreeMarker Template Language (FTL) using variables such as: ${policy.name}, ${execution.resultStatus}, ${result.qualityScore}, etc.

For Jira and ServiceNow, the first test creates a record; subsequent tests update the same record.

Associating Template Groups to Notification Groups

Once your Template Group is ready:

  1. Navigate to Settings -> Notifications -> Notification Groups.
  2. Open or create a Notification Group.
  3. In the group configuration, select your Notification Template Group.
  4. Click Save.

From now on, alerts that use that Notification Group will:

  • Use your custom templates when available.
  • Fall back to system templates for source+channel combinations you haven’t customized.
ConceptRelationship
Notification GroupWho receives the alert and via which channels
Template GroupHow each alert looks for each channel and source type

ADOC Variable Model

Variables are exposed to templates based on policy type and execution context. You can explore them from the Variables tab (right panel) in the editor.

Below is a consolidated reference of the key categories. Exact availability may vary by source type.

Variable Categories

CategoryWhat It DescribesTypical Prefix
Execution ContextResult, severity, timestamps, messagesexecution
Policy InformationPolicy id, name, type, description, tagspolicy
Data Quality ResultsScores, row counts, rule outcomesresult, rule
Reconciliation ResultsCross-system record comparisonresult, reconciliation
Schema DriftStructural changes to assetsdrift
Data DriftDistribution changesdrift
FreshnessExpected vs. actual arrival timesfreshness
Pipeline MonitoringPipeline execution metrics and errorspipeline
Environmental ContextTenant, domains, assets, integrationstenant, asset, integration
Incident ContextIncident entity for failures/warnings onlyincident, severity, status, etc.

Detailed Variable Reference

Note This table focuses on the most heavily used fields. Your Variables panel in ADOC is the source of truth for exact names and availability.

Execution Context

VariableTypeDescriptionExample Usage
execution.resultStatusStringExecution result: SUCCESS / WARNING / FAILED / ERRORED${execution.resultStatus}
execution.severityStringAlert severity: Critical / High / Medium / Low${execution.severity}
execution.startTimeTimestampPolicy execution start time${execution.startTime?string["yyyy-MM-dd HH:mm:ss"]}
execution.endTimeTimestampPolicy execution end time${execution.endTime?string["yyyy-MM-dd HH:mm:ss"]}
execution.idNumberUnique execution identifier${execution.id}
execution.messageStringExecution details / error message${execution.message}

Policy Information

VariableTypeDescriptionExample Usage
policy.nameStringHuman-readable policy name${policy.name}
policy.idNumberUnique policy identifier${policy.id}
policy.typeStringPolicy type (DATA_QUALITY, RECONCILIATION, etc.)${policy.type}
policy.descriptionStringPolicy description${policy.description}
policy.scheduledBooleanWhether the policy runs on schedule${policy.scheduled?string('Yes','No')}
policy.tagsArrayTags assigned to policy${policy.tags?join(", ")}

Data Quality Results

Applies to Data Quality policies.

VariableTypeDescriptionExample Usage
result.qualityScoreNumberOverall quality score (%)${result.qualityScore}%
result.totalRowsScannedNumberTotal rows evaluated${result.totalRowsScanned?string[",###"]}
result.rowsPassedNumberRows that passed all rules${result.rowsPassed}
result.rowsFailedNumberRows that failed one or more rules${result.rowsFailed}
result.warningRowsNumberRows in warning threshold${result.warningRows!0}
result.totalRulesNumberNumber of rules evaluated${result.totalRules}
result.rulesPassedNumberRules that passed${result.rulesPassed}
result.rulesFailedNumberRules that failed${result.rulesFailed}

Rule-level details are typically exposed via a rules collection:

Field NameDescription
rule.nameRule name
rule.descriptionRule description
rule.statusFAILED / PASSED
rule.failedRows(If available) Number of bad rows

Reconciliation Results

Applies to reconciliation policies.

VariableTypeDescriptionExample Usage
result.totalRecordsNumberTotal records compared${result.totalRecords}
result.matchedRecordsNumberRecords that match${result.matchedRecords}
result.unmatchedRecordsNumberRecords that do not match${result.unmatchedRecords}
result.equalityPercentageNumberPercentage of equality${result.equalityPercentage}%
result.leftRowCountNumberRow count in left/source system${result.leftRowCount}
result.rightRowCountNumberRow count in right/target system${result.rightRowCount}
reconciliation.leftDataSourceStringLeft/source system identifier${reconciliation.leftDataSource}
reconciliation.rightDataSourceStringRight/target system identifier${reconciliation.rightDataSource}
reconciliation.joinKeysArrayJoin keys used for matching records${reconciliation.joinKeys?join(", ")}

Schema Drift

Applies to schema drift / structure monitoring policies.

VariableTypeDescriptionExample Usage
drift.addedColumnsArrayNewly added columns${drift.addedColumns?join(", ")}
drift.removedColumnsArrayRemoved columns${drift.removedColumns?join(", ")}
drift.modifiedColumnsArrayColumns with type or constraint changes${drift.modifiedColumns?join(", ")}
drift.lastAlteredTimeTimestampMost recent schema modification${drift.lastAlteredTime?string["yyyy-MM-dd HH:mm:ss"]}
drift.changeCountNumberTotal changes detected${drift.changeCount}

Example snippet:

<#if drift.addedColumns?has_content>
  New columns: ${drift.addedColumns?join(", ")}
</#if>

Data Drift

VariableTypeDescription
drift.driftScoreNumberOverall drift score (change magnitude)
drift.driftedColumnsArrayColumns with significant distribution changes

Freshness

VariableTypeDescription
freshness.expectedTimeTimestampWhen data was expected
freshness.actualTimeTimestampWhen data actually arrived
freshness.delayMinutesNumberDelay in minutes
freshness.expectedBatchCountNumberExpected batch count (if batch-based)
freshness.actualBatchCountNumberActual batch count
freshness.warningThresholdNumberWarning threshold in minutes
freshness.failureThresholdNumberFailure threshold in minutes

Pipeline Monitoring

Applies to pipeline monitoring policies.

VariableTypeDescription
pipeline.idNumberPipeline identifier
pipeline.nameStringPipeline name
pipeline.statusStringSUCCESS, FAILED, etc.
pipeline.startTimeTimestampStart time
pipeline.endTimeTimestampEnd time
pipeline.durationNumberDuration (seconds)
pipeline.recordsProcessedNumberRecords processed
pipeline.bytesProcessedNumberBytes processed
pipeline.errorMessageStringError message for failed executions
pipeline.errorTypeStringError category
pipeline.dependenciesArrayUpstream / downstream dependencies

Environmental Context

VariableTypeDescription
tenant.nameStringTenant name
tenant.idStringUnique tenant identifier
domainsArrayLogical domains associated with the asset
asset.nameStringData asset (table / file / view) name
asset.idStringUnique asset identifier
asset.typeStringAsset type category
integration.nameStringRelated integration name
integration.idStringRelated integration id

Incident Variables (Failures / Warnings Only)

Available only when an incident is created (FAILED / WARNING executions).

VariableTypeDescription
incidentIdStringUnique incident identifier
createdAtTimestampIncident creation time
updatedAtTimestampLast updated time
severityStringIncident severity
statusStringIncident status
totalOccurrencesNumberNumber of times this incident occurred
openInAccelDataUrlStringDeep link to incident / execution in ADOC

These are not available for SUCCESS runs. Design templates either to:

  • Use conditionals to check availability, or
  • Have separate templates for success vs. failure notifications.

Working With Data Types in FreeMarker

Booleans

FreeMarker is strict about booleans. Do not use ${policy.scheduled} directly. Instead:

Scheduled: ${policy.scheduled?string('Yes','No')}

or

Scheduled (raw): ${policy.scheduled?c}

Arrays and Collections

Use ?join for simple lists:

Tags: ${policy.tags?join(", ")}

Or iterate

<#if policy.tags?has_content>
  <#list policy.tags as tag>
    - ${tag}
  </#list>
<#else>
  No tags configured.
</#if>

Null Safety and Defaults

Use ! to provide default values:

Warning rows: ${result.warningRows!0}
Link: ${openInAccelDataUrl!"Not available"}

Use ?? to test if a variable exists:

<#if result.qualityScore??>
  Score: ${result.qualityScore}%
<#else>
  Score not available.
</#if>

Number and Date Formatting

Total Rows: ${result.totalRowsScanned?string[",###"]}
Execution Time: ${execution.startTime?string["yyyy-MM-dd HH:mm:ss"]}

Basic Template Patterns

Interpolation

Policy ${policy.name} finished with status ${execution.resultStatus}.

Conditionals

<#if execution.resultStatus == "SUCCESS">
  ✅ Check passed for ${policy.name}
<#elseif execution.resultStatus == "WARNING">
  ⚠ Check completed with warnings
<#else>
  🚨 Check failed
</#if>

Iteration

<#list rules as rule>
  - ${rule.name}: ${rule.status}
</#list>

Working Template Examples

This section provides complete, production-ready templates for common notification scenarios. These templates demonstrate best practices and can be used as starting points for your own customizations.

Data Quality Email Alert - Comprehensive Format

This template provides detailed data quality information in a well-structured HTML email format.

<!DOCTYPE html>
<html>
<head>
  <style>
    body {   color: #333; }
    .header { background-color: <#if execution.resultStatus == "SUCCESS">#28a745<#elseif execution.resultStatus == "WARNING">#ffc107<#else>#dc3545</#if>; color: white; padding: 20px; text-align: center; }
    .content { padding: 20px; }
    .metric-box { display: inline-block; background: #f8f9fa; padding: 15px; margin: 10px; border-left: 4px solid #007bff; }
    .metric-label { font-size: 12px; color: #666; text-transform: uppercase; }
    .metric-value { font-size: 24px; font-weight: bold; color: #333; }
    .rule-table { width: 100%; border-collapse: collapse; margin-top: 20px; }
    .rule-table th { background: #007bff; color: white; padding: 10px; text-align: left; }
    .rule-table td { padding: 10px; border-bottom: 1px solid #ddd; }
    .status-pass { color: #28a745; font-weight: bold; }
    .status-fail { color: #dc3545; font-weight: bold; }
    .footer { background: #f8f9fa; padding: 15px; margin-top: 20px; text-align: center; font-size: 12px; }
  </style>
</head>
<body>
  <div class="header">
    <h1><#if execution.resultStatus == "SUCCESS">✓<#elseif execution.resultStatus == "WARNING">⚠<#else>✗</#if> Data Quality Alert</h1>
    <h2>${policy.name}</h2>
  </div>
  
  <div class="content">
    <h3>Execution Summary</h3>
    <p><strong>Status:</strong> ${execution.resultStatus}</p>
    <p><strong>Severity:</strong> ${execution.severity}</p>
    <p><strong>Asset:</strong> ${asset.name}</p>
    <p><strong>Execution Time:</strong> ${execution.startTime?string["yyyy-MM-dd HH:mm:ss"]} to ${execution.endTime?string["yyyy-MM-dd HH:mm:ss"]}</p>
    
    <h3>Key Metrics</h3>
    <div>
      <div class="metric-box">
        <div class="metric-label">Quality Score</div>
        <div class="metric-value">${result.qualityScore}%</div>
      </div>
      <div class="metric-box">
        <div class="metric-label">Rows Scanned</div>
        <div class="metric-value">${result.totalRowsScanned?string[",###"]}</div>
      </div>
      <div class="metric-box">
        <div class="metric-label">Rows Failed</div>
        <div class="metric-value">${result.rowsFailed?string[",###"]}</div>
      </div>
      <div class="metric-box">
        <div class="metric-label">Rules Failed</div>
        <div class="metric-value">${result.rulesFailed} / ${result.totalRules}</div>
      </div>
    </div>
    
    <#if result.rulesFailed gt 0>
    <h3>Failed Rules</h3>
    <table class="rule-table">
      <thead>
        <tr>
          <th>Rule Name</th>
          <th>Status</th>
          <th>Description</th>
        </tr>
      </thead>
      <tbody>
        <#list rules as rule>
          <#if rule.status == "FAILED">
        <tr>
          <td>${rule.name}</td>
          <td class="status-fail">${rule.status}</td>
          <td>${rule.description!""}</td>
        </tr>
          </#if>
        </#list>
      </tbody>
    </table>
    </#if>
    
    <#if execution.message?has_content>
    <h3>Additional Details</h3>
    <p>${execution.message}</p>
    </#if>
  </div>
  
  <div class="footer">
    <p>Policy: ${policy.name} (ID: ${policy.id}) | Tenant: ${tenant.name}</p>
    <p><a href="${openInAccelDataUrl!}"  style="color: #007bff;">View in ADOC</a></p>
  </div>
</body>
</html>

Slack Data Quality Alert - Block Kit Format

This template creates a rich, formatted Slack message using Block Kit for data quality alerts.

{
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "<#if execution.resultStatus == 'SUCCESS'>✅<#elseif execution.resultStatus == 'WARNING'>⚠️<#else>🚨</#if> Data Quality: ${policy.name}"
      }
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Status:*\n${execution.resultStatus}"
        },
        {
          "type": "mrkdwn",
          "text": "*Severity:*\n${execution.severity}"
        },
        {
          "type": "mrkdwn",
          "text": "*Quality Score:*\n${result.qualityScore}%"
        },
        {
          "type": "mrkdwn",
          "text": "*Asset:*\n${asset.name}"
        }
      ]
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Execution Details:*\n• Rows Scanned: ${result.totalRowsScanned?string[",###"]}\n• Rows Failed: ${result.rowsFailed?string[",###"]}\n• Rules Failed: ${result.rulesFailed} of ${result.totalRules}\n• Time: ${execution.startTime?string["HH:mm:ss"]}"
      }
    },
    <#if result.rulesFailed gt 0>
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Failed Rules:*\n<#list rules as rule><#if rule.status == 'FAILED'>• ${rule.name}\n</#if></#list>"
      }
    },
    </#if>
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": {
            "type": "plain_text",
            "text": "View in ADOC"
          },
          "url": "${openInAccelDataUrl!}",
          "style": "primary"
        }
      ]
    },
    {
      "type": "context",
      "elements": [
        {
          "type": "mrkdwn",
          "text": "Policy ID: ${policy.id} | Execution ID: ${execution.id} | ${execution.endTime?string["yyyy-MM-dd HH:mm:ss"]}"
        }
      ]
    }
  ]
}

PagerDuty Webhook - Incident Trigger

This template creates a PagerDuty-compatible webhook payload for critical data quality failures.

{
  "routing_key": "YOUR_INTEGRATION_KEY",
  "event_action": "trigger",
  "payload": {
    "summary": "Data Quality Failure: ${policy.name} - ${asset.name}",
    "severity": "<#if execution.severity == 'Critical'>critical<#elseif execution.severity == 'High'>error<#elseif execution.severity == 'Medium'>warning<#else>info</#if>",
    "source": "${tenant.name}",
    "component": "${asset.name}",
    "group": "data-quality",
    "class": "${policy.type}",
    "custom_details": {
      "policy_name": "${policy.name}",
      "policy_id": "${policy.id}",
      "execution_id": "${execution.id}",
      "asset": "${asset.name}",
      "quality_score": "${result.qualityScore}%",
      "rows_scanned": ${result.totalRowsScanned},
      "rows_failed": ${result.rowsFailed},
      "rules_failed": ${result.rulesFailed},
      "total_rules": ${result.totalRules},
      "execution_time": "${execution.startTime?string["yyyy-MM-dd HH:mm:ss"]}",
      "status": "${execution.resultStatus}",
      "severity": "${execution.severity}",
      <#if execution.message?has_content>"message": "${execution.message}",</#if>
      "adoc_link": "${openInAccelDataUrl!}"
    }
  },
  "dedup_key": "adoc-dq-${policy.id}-${execution.id}",
  "client": "Acceldata ADOC",
  "client_url": "${openInAccelDataUrl!}"
}

ServiceNow Incident Creation

This template creates a ServiceNow incident with appropriate field mappings.

{
  "short_description": "Data Quality ${execution.resultStatus}: ${policy.name}",
  "description": "Data Quality Alert Details:\n\nPolicy: ${policy.name}\nAsset: ${asset.name}\nStatus: ${execution.resultStatus}\nSeverity: ${execution.severity}\n\nMetrics:\n- Quality Score: ${result.qualityScore}%\n- Rows Scanned: ${result.totalRowsScanned?string[",###"]}\n- Rows Failed: ${result.rowsFailed?string[",###"]}\n- Rules Failed: ${result.rulesFailed} of ${result.totalRules}\n\nExecution Time: ${execution.startTime?string["yyyy-MM-dd HH:mm:ss"]} to ${execution.endTime?string["yyyy-MM-dd HH:mm:ss"]}\n\n<#if result.rulesFailed gt 0>Failed Rules:\n<#list rules as rule><#if rule.status == 'FAILED'>- ${rule.name}: ${rule.description!''}\n</#if></#list></#if>\n<#if execution.message?has_content>Additional Details:\n${execution.message}\n</#if>\nView in ADOC: ${openInAccelDataUrl!}",
  "impact": "<#if execution.severity == 'Critical'>1<#elseif execution.severity == 'High'>2<#else>3</#if>",
  "urgency": "<#if execution.resultStatus == 'FAILED'>1<#elseif execution.resultStatus == 'WARNING'>2<#else>3</#if>",
  "category": "Data Quality",
  "subcategory": "${policy.type}",
  "caller_id": "${tenant.name}",
  "u_source_system": "Acceldata ADOC",
  "u_policy_name": "${policy.name}",
  "u_policy_id": "${policy.id}",
  "u_execution_id": "${execution.id}",
  "u_asset": "${asset.name}",
  "u_quality_score": "${result.qualityScore}",
  "work_notes": "Execution ID: ${execution.id}\nPolicy ID: ${policy.id}\nTenant: ${tenant.name}\nDomains: ${domains?join(', ')!''}"
}

Pipeline Monitoring Email - Failure Alert

This template provides comprehensive pipeline failure information.

<!DOCTYPE html>
<html>
<head>
  <style>
    body {   color: #333; }
    .alert-banner { background-color: #dc3545; color: white; padding: 20px; text-align: center; font-size: 24px; font-weight: bold; }
    .content { padding: 20px; }
    .info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0; }
    .info-item { background: #f8f9fa; padding: 15px; border-left: 4px solid #dc3545; }
    .label { font-weight: bold; color: #666; font-size: 12px; text-transform: uppercase; }
    .value { font-size: 18px; color: #333; margin-top: 5px; }
    .error-box { background: #fff3cd; border: 1px solid #ffc107; padding: 15px; margin: 20px 0; border-radius: 5px; }
  </style>
</head>
<body>
  <div class="alert-banner">
    🚨 Pipeline Failure Alert
  </div>
  
  <div class="content">
    <h2>${pipeline.name}</h2>
    
    <div class="info-grid">
      <div class="info-item">
        <div class="label">Status</div>
        <div class="value">${pipeline.status}</div>
      </div>
      <div class="info-item">
        <div class="label">Severity</div>
        <div class="value">${execution.severity}</div>
      </div>
      <div class="info-item">
        <div class="label">Start Time</div>
        <div class="value">${pipeline.startTime?string["yyyy-MM-dd HH:mm:ss"]}</div>
      </div>
      <div class="info-item">
        <div class="label">Duration</div>
        <div class="value">${pipeline.duration} seconds</div>
      </div>
      <div class="info-item">
        <div class="label">Records Processed</div>
        <div class="value">${pipeline.recordsProcessed?string[",###"]}</div>
      </div>
      <div class="info-item">
        <div class="label">Bytes Processed</div>
        <div class="value">${pipeline.bytesProcessed?string[",###"]}</div>
      </div>
    </div>
    
    <#if pipeline.errorMessage?has_content>
    <div class="error-box">
      <h3>Error Details</h3>
      <p><strong>Error Type:</strong> ${pipeline.errorType!""}</p>
      <p><strong>Message:</strong> ${pipeline.errorMessage}</p>
    </div>
    </#if>
    
    <#if pipeline.dependencies?has_content>
    <h3>Affected Dependencies</h3>
    <ul>
      <#list pipeline.dependencies as dep>
      <li>${dep}</li>
      </#list>
    </ul>
    </#if>
    
    <p style="margin-top: 30px;">
      <a href="${openInAccelDataUrl!}" style="background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">View Pipeline Details</a>
    </p>
    
    <p style="margin-top: 20px; color: #666; font-size: 12px;">
      Pipeline ID: ${pipeline.id} | Execution ID: ${execution.id} | Tenant: ${tenant.name}
    </p>
  </div>
</body>
</html>

Reconciliation Slack Alert - Mismatch Notification

This template alerts on reconciliation mismatches between source and target systems.

{
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "⚠️ Reconciliation Mismatch Detected"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Policy:* ${policy.name}\n*Status:* ${execution.resultStatus}"
      }
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Source System:*\n${reconciliation.leftDataSource}\n${result.leftRowCount?string[",###"]} records"
        },
        {
          "type": "mrkdwn",
          "text": "*Target System:*\n${reconciliation.rightDataSource}\n${result.rightRowCount?string[",###"]} records"
        }
      ]
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Matched Records:*\n${result.matchedRecords?string[",###"]}"
        },
        {
          "type": "mrkdwn",
          "text": "*Unmatched Records:*\n${result.unmatchedRecords?string[",###"]}"
        },
        {
          "type": "mrkdwn",
          "text": "*Equality:*\n${result.equalityPercentage}%"
        },
        {
          "type": "mrkdwn",
          "text": "*Severity:*\n${execution.severity}"
        }
      ]
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Join Keys:* ${reconciliation.joinKeys?join(', ')}"
      }
    },
    {
      "type": "divider"
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": {
            "type": "plain_text",
            "text": "Investigate in ADOC"
          },
          "url": "${openInAccelDataUrl!}",
          "style": "danger"
        }
      ]
    }
  ]
}

Schema Drift Email - Change Summary

This template provides a clear summary of schema changes detected.

<!DOCTYPE html>
<html>
<head>
  <style>
    body {   color: #333; }
    .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 25px; text-align: center; }
    .content { padding: 20px; max-width: 800px; margin: 0 auto; }
    .change-section { margin: 20px 0; padding: 15px; border-radius: 5px; }
    .added { background: #d4edda; border-left: 5px solid #28a745; }
    .removed { background: #f8d7da; border-left: 5px solid #dc3545; }
    .modified { background: #fff3cd; border-left: 5px solid #ffc107; }
    .change-title { font-weight: bold; font-size: 16px; margin-bottom: 10px; }
    .column-list { list-style: none; padding-left: 20px; }
    .column-list li { padding: 5px 0; }
    .metadata { background: #f8f9fa; padding: 15px; margin-top: 20px; border-radius: 5px; }
  </style>
</head>
<body>
  <div class="header">
    <h1>📊 Schema Drift Detected</h1>
    <h2>${policy.name}</h2>
  </div>
  
  <div class="content">
    <p><strong>Asset:</strong> ${asset.name}</p>
    <p><strong>Detection Time:</strong> ${execution.endTime?string["yyyy-MM-dd HH:mm:ss"]}</p>
    <p><strong>Total Changes:</strong> ${drift.changeCount}</p>
    <p><strong>Last Schema Alteration:</strong> ${drift.lastAlteredTime?string["yyyy-MM-dd HH:mm:ss"]}</p>
    
    <#if drift.addedColumns?has_content>
    <div class="change-section added">
      <div class="change-title">✅ Added Columns (${drift.addedColumns?size})</div>
      <ul class="column-list">
        <#list drift.addedColumns as column>
        <li>${column}</li>
        </#list>
      </ul>
    </div>
    </#if>
    
    <#if drift.removedColumns?has_content>
    <div class="change-section removed">
      <div class="change-title">❌ Removed Columns (${drift.removedColumns?size})</div>
      <ul class="column-list">
        <#list drift.removedColumns as column>
        <li>${column}</li>
        </#list>
      </ul>
    </div>
    </#if>
    
    <#if drift.modifiedColumns?has_content>
    <div class="change-section modified">
      <div class="change-title">⚠️ Modified Columns (${drift.modifiedColumns?size})</div>
      <ul class="column-list">
        <#list drift.modifiedColumns as column>
        <li>${column}</li>
        </#list>
      </ul>
    </div>
    </#if>
    
    <div class="metadata">
      <p><strong>Policy Description:</strong> ${policy.description!""}</p>
      <p><strong>Domains:</strong> ${domains?join(', ')!""}</p>
      <p><a href="${openInAccelDataUrl!}" style="color: #667eea; font-weight: bold;">View Full Details in ADOC →</a></p>
    </div>
  </div>
</body>
</html>

Data Freshness Microsoft Teams Alert

This template creates an Adaptive Card for Microsoft Teams to alert on data freshness issues.

{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.4",
  "body": [
    {
      "type": "Container",
      "style": "<#if execution.resultStatus == 'FAILED'>attention<#elseif execution.resultStatus == 'WARNING'>warning<#else>good</#if>",
      "items": [
        {
          "type": "TextBlock",
          "text": "<#if execution.resultStatus == 'FAILED'>⏰<#elseif execution.resultStatus == 'WARNING'>⚠️<#else>✅</#if> Data Freshness Alert",
          "weight": "bolder",
          "size": "large",
          "wrap": true
        },
        {
          "type": "TextBlock",
          "text": "${policy.name}",
          "size": "medium",
          "wrap": true
        }
      ]
    },
    {
      "type": "FactSet",
      "facts": [
        {
          "title": "Status",
          "value": "${execution.resultStatus}"
        },
        {
          "title": "Severity",
          "value": "${execution.severity}"
        },
        {
          "title": "Asset",
          "value": "${asset.name}"
        },
        {
          "title": "Expected Time",
          "value": "${freshness.expectedTime?string['HH:mm:ss']}"
        },
        {
          "title": "Actual Time",
          "value": "${freshness.actualTime?string['HH:mm:ss']}"
        },
        {
          "title": "Delay",
          "value": "${freshness.delayMinutes} minutes"
        }
      ]
    },
    <#if freshness.expectedBatchCount??>
    {
      "type": "FactSet",
      "facts": [
        {
          "title": "Expected Batches",
          "value": "${freshness.expectedBatchCount}"
        },
        {
          "title": "Actual Batches",
          "value": "${freshness.actualBatchCount}"
        }
      ]
    },
    </#if>
    {
      "type": "TextBlock",
      "text": "Thresholds: Warning at ${freshness.warningThreshold}min, Failure at ${freshness.failureThreshold}min",
      "size": "small",
      "wrap": true,
      "spacing": "medium"
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "View in ADOC",
      "url": "${openInAccelDataUrl!}"
    }
  ]
}

Generic Webhook - Multi-Purpose Format

This template provides a flexible JSON structure suitable for most webhook integrations.

{
  "notification_type": "adoc_alert",
  "timestamp": "${execution.endTime?string['yyyy-MM-dd\'T\'HH:mm:ss\'Z\'']}",
  "alert": {
    "title": "${policy.type}: ${policy.name}",
    "status": "${execution.resultStatus}",
    "severity": "${execution.severity}",
    "message": "${execution.message!''}"
  },
  "policy": {
    "id": ${policy.id},
    "name": "${policy.name}",
    "type": "${policy.type}",
    "description": "${policy.description!''}",
    "scheduled": ${policy.scheduled?c},
    "tags": [<#list policy.tags as tag>"${tag}"<#sep>, </#list>]
  },
  "execution": {
    "id": ${execution.id},
    "start_time": "${execution.startTime?string['yyyy-MM-dd\'T\'HH:mm:ss\'Z\'']}",
    "end_time": "${execution.endTime?string['yyyy-MM-dd\'T\'HH:mm:ss\'Z\'']}",
    "duration_seconds": ${(execution.endTime?long - execution.startTime?long) / 1000}
  },
  "asset": {
    "name": "${asset.name}",
    "id": "${asset.id}",
    "type": "${asset.type}"
  },
  "tenant": {
    "id": "${tenant.id}",
    "name": "${tenant.name}"
  },
  "context": {
    "domains": [<#list domains as domain>"${domain}"<#sep>, </#list>],
    "integration": {
      "name": "${integration.name!''}",
      "id": "${integration.id!''}"
    }
  },
  <#if policy.type == "DATA_QUALITY">
  "results": {
    "quality_score": ${result.qualityScore},
    "total_rows_scanned": ${result.totalRowsScanned},
    "rows_passed": ${result.rowsPassed},
    "rows_failed": ${result.rowsFailed},
    "warning_rows": ${result.warningRows!0},
    "total_rules": ${result.totalRules},
    "rules_passed": ${result.rulesPassed},
    "rules_failed": ${result.rulesFailed}
  },
  </#if>
  "links": {
    "adoc_url": "${openInAccelDataUrl!''}"
  }
}