id: ai-contract-clause-risk-analyzer
namespace: company.legal
description: |
Automates legal risk analysis across enterprise vendor agreements and SaaS master services agreements (MSAs),
using Google Gemini to extract high-risk clauses such as uncapped liability, broad indemnification,
and harsh SLA penalties, enforcing Slack review gates.
triggers:
- id: scheduled_sweep
type: io.kestra.plugin.core.trigger.Schedule
description: Weekly intake sweep for pending procurement contracts. Shipped
disabled by default.
cron: "0 8 * * 1"
disabled: true
- id: webhook_contract_submitted
type: io.kestra.plugin.core.trigger.Webhook
description: Authenticated webhook for legal contract management systems
(Ironclad, DocuSign, CobbleStone).
key: "{{ secret('WEBHOOK_KEY') }}"
inputs:
- id: contract_id
type: STRING
defaults: CTR-2026-8821
description: Unique legal agreement tracking identifier.
- id: vendor_name
type: STRING
defaults: Apex Infrastructure Solutions LLC
description: Name of the counterparty or vendor entity.
- id: contract_text
type: STRING
defaults: |
SECTION 8: LIMITATION OF LIABILITY
Except for breaches of confidentiality or gross negligence, neither party's liability under this Agreement shall exceed twelve (12) months of fees paid. Notwithstanding the foregoing, Customer's indemnity obligations under Section 9 shall be uncapped and without limitation.
SECTION 9: INDEMNIFICATION
Customer shall defend, indemnify, and hold harmless Vendor against any third-party claims, damages, liabilities, and legal expenses arising out of Customer's use of the Hosted Platform, including any indirect, incidental, or consequential damages claimed by third parties.
SECTION 12: TERM AND RENEWAL
This Agreement shall automatically renew for successive three (3) year periods unless either party provides written notice of non-renewal at least ninety (90) days prior to the expiration of the initial term. Early termination shall incur an immediate acceleration of all remaining fees through the term.
description: Raw legal contract text or clause excerpts under review.
tasks:
- id: audit_contract_clauses
type: io.kestra.plugin.ai.agent.AIAgent
description: Uses Gemini 2.5 Flash to inspect contractual language for uncapped
liability, asymmetric indemnification, and restrictive renewal terms.
provider:
type: io.kestra.plugin.ai.provider.GoogleGemini
apiKey: "{{ secret('GEMINI_API_KEY') }}"
modelName: gemini-2.5-flash
configuration:
temperature: 0.1
maxToken: 4096
responseFormat:
type: JSON
jsonSchema:
type: object
required:
- contract_id
- vendor_name
- risk_tier
- liability_capped
- uncapped_indemnity_detected
- auto_renewal_lockin_detected
- high_risk_clauses
- counsel_summary
properties:
contract_id:
type: string
vendor_name:
type: string
risk_tier:
type: string
enum:
- HIGH
- MEDIUM
- LOW
liability_capped:
type: boolean
uncapped_indemnity_detected:
type: boolean
auto_renewal_lockin_detected:
type: boolean
high_risk_clauses:
type: array
items:
type: object
required:
- clause_name
- excerpt
- risk_explanation
- fallback_recommendation
properties:
clause_name:
type: string
excerpt:
type: string
risk_explanation:
type: string
fallback_recommendation:
type: string
counsel_summary:
type: string
prompt: |
You are an expert Senior Commercial Technology Counsel reviewing an enterprise vendor agreement.
Analyze the contract text below for commercial hazards, asymmetric liabilities, and aggressive terms:
Contract ID: {{ inputs.contract_id }}
Vendor: {{ inputs.vendor_name }}
Agreement Text:
\"\"\"
{{ inputs.contract_text }}
\"\"\"
Audit Checklist:
1. Limitation of Liability: Is liability mutual and capped, or are there asymmetric carved-out indemnities?
2. Indemnification: Does the customer accept broad or uncapped indemnification for indirect/consequential damages?
3. Term & Auto-Renewal: Are there multi-year automatic renewals with strict advance notice windows (>=60 days) or early termination fee acceleration?
4. Assign an objective risk_tier:
- HIGH: Uncapped customer indemnity, asymmetric liability carve-outs, or multi-year fee acceleration.
- MEDIUM: Ambiguous renewal windows or non-standard governing law.
- LOW: Standard mutual liability caps, balanced indemnification, and flexible termination.
- id: render_legal_risk_brief
type: io.kestra.plugin.scripts.python.Script
description: Formats structured contract risk metrics into an executive legal
assessment brief stored in execution artifacts.
taskRunner:
type: io.kestra.plugin.core.runner.Process
script: |
import json
raw_response = """{{ outputs.audit_contract_clauses.text }}"""
data = json.loads(raw_response, strict=False)
cid = data.get("contract_id", "{{ inputs.contract_id }}")
vendor = data.get("vendor_name", "{{ inputs.vendor_name }}")
tier = data.get("risk_tier", "HIGH")
capped = "YES" if data.get("liability_capped") else "NO (ASYMMETRIC)"
indemnity = "FLAGGED (UNCAPPED)" if data.get("uncapped_indemnity_detected") else "Standard"
renewal = "FLAGGED (AUTO-LOCKIN)" if data.get("auto_renewal_lockin_detected") else "Standard"
summary = data.get("counsel_summary", "")
clauses_md = []
for c in data.get("high_risk_clauses", []):
clauses_md.append(f"""### {c.get('clause_name')}
- **Language**: *\"{c.get('excerpt')}\"*
- **Legal Risk**: {c.get('risk_explanation')}
- **Recommended Redline**: {c.get('fallback_recommendation')}
""")
clauses_section = "\n".join(clauses_md) or "No high-risk clauses identified."
markdown = f"""# CONTRACT CLAUSE LEGAL RISK ASSESSMENT
## Executive Summary
- **Contract ID**: `{cid}`
- **Counterparty**: **{vendor}**
- **Overall Risk Classification**: **{tier}**
- **Liability Capped**: {capped}
- **Indemnity Exposure**: {indemnity}
- **Renewal Lock-in**: {renewal}
### Legal Counsel Assessment
{summary}
## High-Risk Clause Analysis & Redline Guidance
{clauses_section}
"""
with open("contract-risk-assessment.md", "w") as f:
f.write(markdown)
print("Generated contract-risk-assessment.md")
outputFiles:
- contract-risk-assessment.md
- id: legal_signoff_gate
type: io.kestra.plugin.core.flow.If
description: Routes contracts with high-risk clauses directly to general counsel
review channels in Slack.
condition: "{{ json(outputs.audit_contract_clauses.text).risk_tier == 'HIGH' }}"
then:
- id: alert_general_counsel
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Alerts general counsel channel with contract risk breakdown and
redline guidance.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"channel": "#legal-contract-review",
"text": "🚨 *High-Risk Contract Trapped at Gate*: `{{ inputs.contract_id }}` ({{ inputs.vendor_name }})\n*Risk Tier*: *HIGH*\n*Uncapped Indemnity*: {{ json(outputs.audit_contract_clauses.text).uncapped_indemnity_detected }}\n*Renewal Lock-in*: {{ json(outputs.audit_contract_clauses.text).auto_renewal_lockin_detected }}\n*Counsel Summary*: {{ json(outputs.audit_contract_clauses.text).counsel_summary }}\nRedline brief generated in Kestra storage."
}
else:
- id: notify_procurement_standard
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatches standard approval notice to procurement operations channel.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"channel": "#procurement-feed",
"text": "✅ *Contract Passed Routine Review*: `{{ inputs.contract_id }}` ({{ inputs.vendor_name }}). Risk tier: {{ json(outputs.audit_contract_clauses.text).risk_tier }}. Standard legal terms confirmed."
}
errors:
- id: alert_on_failure
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Alerts on-call engineering channel if contract analysis encounters
an unhandled error.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"channel": "#legalops-alerts",
"text": "⚠️ *Pipeline Error*: Contract risk analyzer `{{ flow.id }}` failed on execution `{{ execution.id }}`."
}