Governing OpenAI agents: authority, tools and audit evidence
An agent that calls tools decides for itself which ones to call. That is what makes it useful — and why a guardrail it can choose to skip is not a guardrail. The authority decision is called from your code, not from the prompt.
Agents built on OpenAI models reason and then act through tool calls: search, read, write, send, execute. It is a powerful model, because the agent picks the sequence itself — and that very choice is what creates the governance problem.
A tool the model can decide not to call is not a guardrail. Exposing a check-compliance function and hoping the model remembers it moves a compliance decision into a prompt instruction — the least reliable place in the system. The authority layer must be called by your code, deterministically, before the sensitive action.
The integration scenario
Two paths. The first, recommended: a deterministic HTTP call inside the implementation of your sensitive tools. The second, complementary: wire in our MCP server to give the agent regulatory reading capabilities — jurisdiction mapping, compliance roadmap — while the decision itself stays in your code.
- 1
Generate your organization's profile
The free assessment (start here) establishes your jurisdictions, sector and data categories. Decisions are rendered against that profile — not against generic rules.
- 2
Put the call inside the tool implementation
Not in the tool description, not in the system prompt: in the function body. The first line of your
exportCustomerRecord()asks for authorization; the rest only runs on ALLOW.curl -X POST https://structureclerk.ca/api/v1/authority/decide \ -H 'Authorization: Bearer $API_KEY' \ -H 'Content-Type: application/json' \ -d '{"agent":{"id":"support-agent","autonomy_level":3},"action":{"type":"export.customer_record","data_categories":["personal"]},"context":{"jurisdictions":["CA_QC"],"sector":"tech"}}' - 3
Handle the four verbs explicitly
ALLOW executes. APPROVE surfaces a validation request to the interface. DENY refuses and returns the reason to the model, which can explain it to the user. ESCALATE notifies and holds. Keep the evidence identifier with your execution trace.
- 4
Give the model something to explain with
The returned reason is a stable identifier —
human_only_decision, for instance — which your code turns into a sentence for the user. The agent does not invent a justification — it reports a decision.
The contract, in your code
// Le garde-fou est dans VOTRE code, pas dans le prompt.
const decision = await fetch(
"https://structureclerk.ca/api/v1/authority/decide",
{ method: "POST",
headers: { "Content-Type": "application/json",
Authorization: `Bearer ${process.env.SC_API_KEY}` },
body: JSON.stringify({
agent: { id: "support-agent", autonomy_level: 3 },
action: { type: "export.customer_record",
data_categories: ["personal"] },
context: { jurisdictions: ["CA_QC"], sector: "tech" },
}) }
).then(r => r.json());
if (decision.decision === "DENY") return refuse(decision.reason);
if (decision.decision !== "ALLOW") return requestApproval(decision);
// evidence_id conservé avec la trace d'exécution
await performExport();The decision is advisory — StructureClerk decides, your infrastructure enforces. Here, your infrastructure is literally the if above: you enforce, and that is what keeps the layer out of your critical path if it is ever unavailable.
| Decision | What your code does |
|---|---|
| ALLOW | Executes the action, keeps the evidence identifier |
| APPROVE | Surfaces a validation request to the interface |
| DENY | Refuses, returns the reason to the model to explain |
| ESCALATE | Notifies the owner, holds the action |
Regulatory reading over MCP
Beyond the decision, our MCP server exposes 5 tools the agent can consult freely — jurisdiction mapping, compliance check, roadmap, algorithmic assessment, and the decision itself. Those reads are safe to leave to the model's discretion: they inform, they authorize nothing.
Every decision leaves Ed25519-signed evidence, verifiable by anyone on our verification page, without an account. That is the answer to prove your agent was allowed — a chain your counterparty checks without you.
+Why not expose the decision as a tool the model calls?
Because a tool the model can choose not to call is not a guardrail: the day it forgets, the action happens with no decision and no evidence. Regulatory reading can be a tool; authorization must sit in the action's execution path.
+How do I audit an OpenAI agent's actions?
Every decision produces Ed25519-signed, timestamped, chained evidence carrying the rule applied and the reason. Keep the identifier with your execution trace: you get a publicly verifiable audit trail, independent of your own logs.
+What happens if the decision call fails?
That is your code's call, and a real architectural choice: failing closed (not executing the sensitive action) is the prudent behaviour for irreversible actions; failing open can be defended for low-risk ones. Because the layer sits outside your critical path, both options stay available.
+Does the engine see our data or prompts?
No. The request describes the action as metadata: type, data categories, jurisdictions, sector, autonomy level. Neither content, nor prompts, nor customer data are transmitted — the decision does not need them.
Start with the profile
The assessment is what personalizes the decisions your agents receive. It is free, and only needs your website address.