Using Mandaitor with OpenAI Agents
Using Mandaitor with OpenAI Agents
- Understand Mandaitor's action, resource, and mandate model.
- Basic familiarity with OpenAI function/tool calling.
- explain where Mandaitor sits in an OpenAI tool-calling loop
- gate a tool call with a Proof-of-Mandate verification before it executes
- map OpenAI agent concepts onto Mandaitor's authority model
OpenAI's function calling, the Assistants API, and the Agents SDK all share one shape: the model decides to call a tool you defined, and your code runs it. Mandaitor does not replace any of that. It adds one step inside your tool handler — a verification that the proposed action is inside a granted mandate — so a sensitive tool call only runs when a specific principal has authorized it. You keep OpenAI; you add verifiable authority.
Where Mandaitor sits in the loop
The key idea: the model can propose anything, but execution happens in your code, and that is where the mandate check belongs. The model never sees credentials or makes the authority decision — it only receives the result of the check as a tool output it can reason about.
The tool call is a request, not a right
OpenAI decides a tool should run and supplies arguments. Nothing has happened yet — it is a proposal your code can accept or refuse.
Check the specific action
Your handler calls verify() with the delegate, action, resource, and context. The decision is tied to a real, revocable mandate.
Act, then keep evidence
On ALLOW, perform the side effect and store the signed proof. On DENY, return the reason to the model so it can adapt or escalate.
The pattern in code
Define your OpenAI tool as usual. Inside the handler that OpenAI calls, verify before you act. The Mandaitor call below uses the real @mandaitor/sdk verify() method.
import { MandaitorClient } from "@mandaitor/sdk";
const mandaitor = new MandaitorClient({
apiKey: process.env.MANDAITOR_API_KEY,
tenantId: "tnt_your_tenant_id",
});
// The function/tool you exposed to the OpenAI model. OpenAI calls this with the
// arguments it chose; you decide whether it is allowed to actually run.
async function issueRefundTool(args: { customerId: string; amount: number; currency: string }) {
const result = await mandaitor.verify({
delegate_subject_id: "construction:agent:support-copilot",
action: "billing.refund.issue",
resource: `example:customer:${args.customerId}/*`,
context: { amount: args.amount, currency: args.currency, channel: "chat" },
});
if (result.decision !== "ALLOW") {
// Return a tool result the model can reason about — do NOT perform the action.
return { authorized: false, reason_codes: result.reason_codes };
}
const refund = await billing.issueRefund(args); // your real side effect
return { authorized: true, refund, proof: result.proof }; // keep proof as evidence
}
The model receives { authorized: false, reason_codes: [...] } as an ordinary tool result and can explain the denial, ask for approval, or try a smaller action — while your systems stay protected. On success, result.proof is the signed artifact you keep for audit.
Try a mandate live
The mandate the refund tool is checked against is created once by a principal. The demo below runs the real @mandaitor/react creator against an in-browser mock — edit the code to change the actions or resources.
<DocuWrapper> <MandateCreator availableActions={[ { id: "billing.refund.issue", label: "Issue refund", description: "Refund a customer charge" }, { id: "billing.invoice.read", label: "Read invoice", description: "Read a customer invoice" }, ]} defaultResources={["example:customer:cust_demo/*"]} /> </DocuWrapper>
| OpenAI concept | Mandaitor concept |
|---|---|
| Tool / function call | A concrete action on a resource to verify |
| Assistant / agent identity | The delegate (e.g. construction:agent:support-copilot) |
| Function arguments | context passed to verify() (amount, purpose, channel) |
| Tool result returned to the model | The decision + reason_codes, so the model can adapt |
| Nothing built-in | The signed proof you keep as evidence |
In an OpenAI tool-calling loop, where does the Mandaitor check belong?
- Inside your tool handler, before the side effect runs — the model proposes, but your code decides and verifies.
- In the model's system prompt, so the model authorizes itself.
- After the action has already been performed, as a log entry.
Reveal answer
The model only proposes tool calls; execution happens in your code. Verifying inside the handler — before the side effect — means an unauthorized action never runs, and the model receives the decision as a tool result it can reason about.
From concept to implementation
This lesson is the mental model. When you are ready to build, the guides cover the concrete calls end to end.
- Getting Started — install the SDK, create a mandate, run your first
verify(). - Verifying Actions — the full verification contract and response fields.
- Proof-of-Mandate — what the
proofartifact is and how to store it.
Save your learning progress
Mark this lesson as complete to update the Academy overview without requiring an account.