id: ai-gdpr-breach-notification
namespace: company.legal
description: |
Generate GDPR-compliant data breach notifications for the supervisory
authority (Article 33) and affected data subjects (Article 34).
inputs:
- id: breach_description
type: STRING
displayName: Describe the data breach incident
defaults: |
On 2026-03-05 at 14:32 UTC, our security team detected unauthorized
access to the customer database via a compromised admin account.
The attacker exfiltrated approximately 12,000 records containing
full names, email addresses, hashed passwords (bcrypt), and postal
addresses. The breach was contained at 16:45 UTC by revoking the
compromised credentials and blocking the attacker's IP range. The
affected customers are EU residents subscribed to our SaaS platform.
- id: severity
type: SELECT
displayName: Estimated severity
defaults: High
values:
- Low, unlikely to result in risk to individuals
- Medium, risk to individuals but limited scope
- High, high risk to rights and freedoms
- Critical, large-scale, sensitive data exposed
- id: dpo_email
type: STRING
displayName: DPO email address
defaults: dpo@example.com
- id: supervisory_authority
type: SELECT
displayName: Lead supervisory authority
defaults: CNIL (France)
values:
- CNIL (France)
- BfDI (Germany)
- ICO (United Kingdom)
- AEPD (Spain)
- Garante (Italy)
- APD/GBA (Belgium)
- UODO (Poland)
- DPC (Ireland)
- AP (Netherlands)
- Other
tasks:
# Step 1, Triage the breach and generate structured metadata.
# Uses JSON structured output to produce machine-readable triage data
# that downstream systems (ticketing, SIEM) can consume.
- id: breach_triage
type: io.kestra.plugin.ai.agent.AIAgent
description: Triage the breach and extract structured metadata
# Mistral Large, European AI model by Mistral AI (Paris, France).
# Legal breach data stays within EU jurisdiction.
#
# For air-gapped compliance, switch to a local model:
# type: io.kestra.plugin.ai.provider.Ollama
# endpoint: http://ollama:11434
# modelName: mistral-large
provider:
type: io.kestra.plugin.ai.provider.MistralAI
configuration:
temperature: 0.0
maxToken: 2048
responseFormat:
type: JSON
jsonSchema:
type: object
required:
- breach_category
- data_types_affected
- approximate_records
- requires_authority_notification
- requires_subject_notification
- notification_deadline_hours
- risk_level
properties:
breach_category:
type: string
enum:
- Confidentiality breach
- Integrity breach
- Availability breach
- Combined breach
data_types_affected:
type: array
items:
type: string
approximate_records:
type: integer
requires_authority_notification:
type: boolean
description: Article 33, notify unless unlikely to result in risk
requires_subject_notification:
type: boolean
description: Article 34, notify if high risk to rights and freedoms
notification_deadline_hours:
type: integer
description: Hours remaining from breach awareness (72h max per Art. 33)
risk_level:
type: string
enum:
- No risk
- Low risk
- Risk
- High risk
systemMessage: |
You are a GDPR breach response specialist. Analyze the breach and
produce a JSON triage assessment. Follow EDPB Guidelines 9/2022 on
personal data breach notification.
Key rules:
- Article 33(1): Notify the supervisory authority within 72 hours
UNLESS the breach is unlikely to result in a risk to natural persons
- Article 34(1): Notify data subjects when breach is likely to result
in a HIGH RISK to their rights and freedoms
- Consider: nature of data, volume, whether data was encrypted,
containment speed, and potential consequences
prompt: |
Triage this data breach:
{{ inputs.breach_description }}
Estimated severity: {{ inputs.severity }}
# Step 2, Generate the Article 33 supervisory authority notification.
# This follows the exact structure required by most EU DPAs.
- id: authority_notification
type: io.kestra.plugin.ai.agent.AIAgent
description: Draft the Article 33 notification to the supervisory authority
provider:
type: io.kestra.plugin.ai.provider.MistralAI
configuration:
temperature: 0.1
maxToken: 4096
contentRetrievers:
- type: io.kestra.plugin.ai.retriever.TavilyWebSearch
maxResults: 3
systemMessage: |
You are a senior GDPR legal counsel drafting an Article 33
notification to a supervisory authority. You must produce a formal,
complete notification document.
The notification MUST contain all elements required by Article 33(3):
(a) Nature of the breach including categories and approximate number
of data subjects and records
(b) Name and contact details of the DPO or other contact point
(c) Likely consequences of the breach
(d) Measures taken or proposed to address the breach and mitigate
adverse effects
Use web search to find the specific notification form or portal for
the target supervisory authority.
prompt: |
Draft a formal Article 33 breach notification for:
SUPERVISORY AUTHORITY: {{ inputs.supervisory_authority }}
BREACH TRIAGE DATA:
{{ outputs.breach_triage.textOutput }}
ORIGINAL INCIDENT DESCRIPTION:
{{ inputs.breach_description }}
Format as a formal notification document in Markdown, ready to be
submitted. Include placeholder fields for [DPO_NAME], [DPO_EMAIL],
[DPO_PHONE], and [COMPANY_NAME] that the legal team will fill in.
# Step 3, Generate the Article 34 data subject notification if required.
- id: subject_notification
type: io.kestra.plugin.ai.agent.AIAgent
description: Draft the Article 34 notification to affected data subjects
provider:
type: io.kestra.plugin.ai.provider.MistralAI
configuration:
temperature: 0.2
maxToken: 3072
systemMessage: |
You are a GDPR legal counsel drafting an Article 34 data subject
notification. This is a communication sent DIRECTLY to affected
individuals.
Article 34(2) requirements, the communication must:
- Use clear and plain language (not legal jargon)
- Describe the nature of the breach
- Provide the DPO's contact details
- Describe likely consequences
- Describe measures taken and recommended self-protection steps
Article 34(3) exceptions, notification is NOT required if:
(a) Appropriate technical measures rendered data unintelligible
(e.g., encryption)
(b) Subsequent measures ensure high risk is no longer likely
(c) It would involve disproportionate effort (use public communication)
Assess whether an exception applies. If so, state which one and why.
If not, draft the notification.
prompt: |
Based on the breach triage:
{{ outputs.breach_triage.textOutput }}
And the original incident:
{{ inputs.breach_description }}
Either:
1. Draft an Article 34 data subject notification in clear, plain
language (if high risk is confirmed), OR
2. Explain which Article 34(3) exception applies and why notification
to data subjects is not required.
Include recommended self-protection steps for data subjects
(password changes, monitoring, etc.) if notification is needed.
# Step 4, Email the Article 33 notification to the legal/DPO team.
# This is the internal notification so legal counsel can review, finalize,
# and submit to the supervisory authority within the 72-hour deadline.
- id: email_authority_notification
type: io.kestra.plugin.email.MailSend
to: "{{ inputs.dpo_email }}"
subject: "[URGENT] GDPR Breach, Article 33 Notification Draft, {{
inputs.supervisory_authority }}"
htmlTextContent: |
<h2>Article 33, Supervisory Authority Notification Draft</h2>
<p><strong>Supervisory Authority:</strong> {{ inputs.supervisory_authority }}</p>
<p><strong>Severity:</strong> {{ inputs.severity }}</p>
<hr>
<pre>{{ outputs.authority_notification.textOutput }}</pre>
<hr>
<p><em>This draft was generated automatically. Review, complete the
placeholder fields, and submit to the supervisory authority before
the 72-hour deadline expires.</em></p>
# Step 5, Retrieve affected customer contacts from HubSpot CRM.
- id: search_affected_contacts
type: io.kestra.plugin.hubspot.contacts.Search
properties:
- email
- firstname
- lastname
fetchAllPages: true
# Step 6, Extract email addresses from the HubSpot search results.
- id: extract_emails
type: io.kestra.plugin.scripts.python.Script
containerImage: python:3.11-slim
inputFiles:
contacts.ion: "{{ outputs.search_affected_contacts.uri }}"
script: |
import json
with open("contacts.ion") as f:
contacts = [json.loads(line) for line in f if line.strip()]
emails = [c["email"] for c in contacts if c.get("email")]
with open("{{ outputDir }}/emails.txt", "w") as f:
f.write(",".join(emails))
outputFiles:
- emails.txt
# Step 7, Email the Article 34 notification to affected data subjects.
- id: email_subject_notification
type: io.kestra.plugin.email.MailSend
to: "{{ read(outputs.extract_emails.outputFiles['emails.txt']) }}"
subject: "Important: Security Incident Notification, Action Required"
htmlTextContent: |
<pre>{{ outputs.subject_notification.textOutput }}</pre>
pluginDefaults:
- type: io.kestra.plugin.ai.provider.MistralAI
values:
apiKey: "{{ secret('MISTRAL_API_KEY') }}"
modelName: mistral-large-2512
- type: io.kestra.plugin.ai.retriever.TavilyWebSearch
values:
apiKey: "{{ secret('TAVILY_API_KEY') }}"
- type: io.kestra.plugin.hubspot.contacts.Search
values:
apiKey: "{{ secret('HUBSPOT_API_KEY') }}"
- type: io.kestra.plugin.email.MailSend
values:
host: "{{ secret('SMTP_HOST') }}"
port: 465
username: "{{ secret('SMTP_USERNAME') }}"
password: "{{ secret('SMTP_PASSWORD') }}"
from: "{{ secret('LEGAL_TEAM_EMAIL') }}"