How We Stopped an AI Agent from Paying a Sanctioned Vendor — Automatically
How We Stopped an AI Agent from Paying a Sanctioned Vendor — Automatically
Category: Financial Services | Reading time: 6 min
The $2.3 Million Near-Miss Nobody Talks About
In 2023, a mid-sized logistics company nearly wired $2.3 million to a vendor that had been added to the OFAC Specially Designated Nationals (SDN) list three weeks earlier. The accounts payable team had processed over 400 payments that month. Nobody had noticed. The wire was flagged — by accident — when a compliance officer happened to spot the vendor name in a batch report.
That company was lucky. Most aren't.
As AI agents take over accounts payable workflows, the exposure grows. An AI that can process 10,000 invoices a day can also make 10,000 compliance errors a day — at machine speed, with no fatigue, and no gut feeling that something is wrong.
The question isn't whether your AI will encounter a sanctioned vendor. It will. The question is: what happens next?
Why Prompt Engineering Won't Save You
The instinct of most teams building AI payment workflows is to add compliance instructions to the system prompt: "Never pay sanctioned vendors. Always check OFAC status before initiating payment."
This doesn't work. Here's why:
- Prompts are suggestions. Large language models don't enforce rules — they follow instructions probabilistically. Under certain input conditions, edge cases, or model updates, the instruction can be ignored.
- Prompts don't compose. When you chain agents together — invoice extraction, vendor lookup, payment submission — there's no guarantee the OFAC instruction in step one survives into step three.
- Prompts aren't auditable. When a regulator asks "how did you ensure no payments went to sanctioned entities?", you can't hand them a system prompt and call it a control.
Compliance requires enforcement, not suggestions.
Policy-as-Code: The Only Real Control
The Taos governance kernel treats compliance rules as first-class code, evaluated at runtime using Rego — the same policy language used by the Open Policy Agent ecosystem, embedded directly in the kernel via Regorus (no external OPA server required).
Here is the actual OFAC rule from the vendor payment policy:
# Hard deny — no override, no approval escalation
deny_blocked_vendor {
input.vendor_status == "blocked"
}
deny_blocked_vendor {
input.is_ofac_cleared == false
}
action = "deny" {
deny_blocked_vendor
}
This rule is not a prompt. It is evaluated deterministically, in microseconds, before any payment step executes. If vendor_status is "blocked" or is_ofac_cleared is false, the workflow raises a PolicyDeniedError and stops — immediately, completely, with no path forward.
No LLM involvement. No probabilistic reasoning. No exceptions.
What the Enforcement Looks Like in Practice
When a payment agent encounters a blocked vendor:
- Policy check fires — Rego evaluates
input.vendor_statusandinput.is_ofac_clearedagainst the rule - Hard deny returned —
action = "deny",allow = false,rule_fired = "deny_blocked_vendor",denial_mode = "hard" - PolicyDeniedError raised — the agent run terminates immediately; the LLM never sees the denial reason
- Saga compensation runs — any steps already completed (e.g. invoice extraction) are rolled back in LIFO order
- Audit event written — immutable record: timestamp, rule that fired, vendor ID, invoice ID, submitting user, policy version
- No payment is created — the ledger is never touched
The entire sequence takes under 100ms. The agent never "decides" anything — the kernel enforces.
Hard Denial: Why the LLM Must Not See the Reason
This is a subtle but critical point covered in our policy enforcement model and architecture overview: when a denial is soft, the kernel returns a structured denial dict to the LLM, which can reformulate it in natural language — useful for conversational helper steps. But for regulatory denials like OFAC, soft mode is dangerous:
- The LLM may rephrase the regulatory reason in vague or context-sensitive language
- The audit log reason and what the user heard diverge — a compliance problem
- The LLM may omit the
rule_firedidentifier needed for remediation
OFAC-related step definitions carry denial_mode = "hard". This means when the kernel sends PolicyDenied, the ADK/Genkit plugin raises a typed PolicyDeniedError immediately — the agent run terminates, saga compensation fires, and the verbatim regulatory reason goes directly to the calling code and audit log. The LLM never touches it.
# Hard denial — raised by the plugin before the LLM sees anything
class PolicyDeniedError(Exception):
tool_name: str # which tool was denied
reason: str # verbatim kernel reason — no LLM reformulation
rule_fired: str # "deny_blocked_vendor"
Soft denial exists for non-regulatory contexts (e.g. an internal policy suggesting an alternative). Hard denial is the only appropriate mode for sanctions enforcement.
The Audit Trail That Satisfies Regulators
Every policy decision in Taos is written to an immutable, hash-chained audit log. For each blocked payment, the record contains:
- The exact Rego rule that fired (
deny_blocked_vendor) - The policy version evaluated (linked to the bundle in the control plane)
- The input that triggered the rule (
vendor_status,is_ofac_cleared) - The full OBO (On-Behalf-Of) chain — every human and system that authorised the workflow up to the point of denial
- A cryptographic hash linking to the previous audit event (tamper-evident)
When your compliance team or an external auditor asks "prove that no payments went to sanctioned vendors between January and March", you can produce a cryptographically verifiable log of every policy evaluation, every denial, and every rule version in force at the time.
Keeping the Policy Current
OFAC adds and removes entities continuously. Your enforcement policy needs to stay current without requiring code deployments.
With Taos, policy bundles are edited in the control plane UI — a structured editor where compliance teams can update Rego rules, add new vendor status checks, and publish new versions. The kernel picks up the new version on the next evaluation cycle. No deployment. No restart. No engineering ticket.
The payment demo we built shows this in practice: edit a rule in the UI, call POST /api/policy/reload, and the next payment evaluation runs against the updated policy — the engine reloads in milliseconds.
The Bottom Line
AI agents in accounts payable are not optional anymore — the efficiency gains are too large to ignore. But deploying them without runtime policy enforcement is a compliance liability, not an innovation.
The Taos governance kernel makes OFAC enforcement a hard, auditable, code-level guarantee rather than a best-effort prompt. Your AI processes faster. Your compliance team sleeps better. And when the regulator asks for proof, you have it.
Ready to see it in action? The vendor payment demo — including live OFAC deny with full audit trail — is available on GitHub.
Tags: AI governance, OFAC compliance, accounts payable automation, policy-as-code, Rego, financial services AI