Using Mandaitor with OpenAI Agents
Using Mandaitor with OpenAI Agents
- Understand Mandaitor's mandate, tool and execution model.
- Basic familiarity with OpenAI function/tool calling.
- explain where Mandaitor sits in an OpenAI tool-calling loop
- have a tool call carried out by Mandaitor instead of by your own code
- map OpenAI agent concepts onto Mandaitor's execution model
OpenAI's function calling, the Responses API and the Agents SDK share one shape: the model decides to call a tool you defined, and your code runs it. Mandaitor changes what "runs it" means for a business operation. Your tool handler holds no credential for the target system and performs no write. It asks Mandaitor, which reads the real record, decides against the agent's mandates, asks a person where a mandate says so, performs the write, and returns the outcome with a signed receipt. You keep OpenAI; the write moves to Mandaitor.
Where Mandaitor sits in the loop
The model can propose anything. Your handler passes the proposal on as a request that names the operation and the record, never the record's facts. Mandaitor decides on what it reads from the target system, so neither the model nor your handler can talk an amount down, and neither ever sees a credential that could write without asking.
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.
On the record, not the arguments
Your handler names the tool and the record. Mandaitor reads the record, decides against the agent's mandates, and writes with a credential neither the model nor your code holds.
A result it can reason about
Written, with a receipt; waiting for a person's approval; or refused, with the reason. The model can report it, wait, or stop.
The pattern in code
Define the function as usual. Its handler asks Mandaitor with the agent's own identity, through the SDK's executions client (@mandaitor/sdk 5.1.0 or later), and returns what became of the request:
import { MandaitorExecutionsClient } from "@mandaitor/sdk";
// The agent's identity: its DID and the key it registered. No SAP credential anywhere.
const executions = new MandaitorExecutionsClient({
agent: {
did: process.env.AGENT_DID!,
keyId: process.env.AGENT_KEY_ID!,
privateKey: process.env.AGENT_PRIVATE_KEY_PEM!,
},
});
// The function you expose to the model.
export const releaseRequisitionTool = {
type: "function",
name: "release_purchase_requisition",
description: "Release a purchase requisition in SAP, so that purchasing can order it.",
parameters: {
type: "object",
properties: {
purchase_requisition: { type: "string", description: "The requisition's number" },
purpose: { type: "string", description: "Why, in one sentence, for the approver" },
},
required: ["purchase_requisition"],
additionalProperties: false,
},
};
// OpenAI calls this with the arguments it chose. It asks Mandaitor, and never writes itself.
export async function releaseRequisition(
args: { purchase_requisition: string; purpose?: string },
callId: string, // the model's tool call id
) {
const { execution: requested } = await executions.requestExecution({
tool: "sap.purchase_requisition.release",
arguments: { purchase_requisition: args.purchase_requisition },
// A retry of the same tool call reuses its key, so it is never written twice.
idempotency_key: callId,
purpose: args.purpose,
});
const execution = await executions.waitForExecution(requested.execution_id);
// What the model sees: the outcome and why. Never a credential.
return { state: execution.state, reason: execution.state_reason, result: execution.result };
}
The model receives the state as an ordinary tool result. SUCCEEDED comes with the released requisition. AWAITING_APPROVAL means a person decides, and the model can say so. DENIED comes with a reason it can explain, such as AMOUNT_ABOVE_LIMIT. The signed receipt stays in the execution, for your records.
If your agent framework can connect to remote MCP servers, it can connect to Mandaitor's MCP server instead. Each tool call then becomes an execution with no handler code at all.
Try a mandate live
The mandate the agent acts under is written once, by a person. 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: "procurement.requisition.release", label: "Release requisition", description: "Release a purchase requisition for ordering" }, ]} defaultResources={["procurement:plant:1000/requisition:*"]} /> </DocuWrapper>
| OpenAI concept | Mandaitor concept |
|---|---|
| Tool / function call | A tool request: one tool and one record |
| Assistant / agent identity | A registered agent, named by its DID, with a key only it holds |
| Function arguments | The record's identifier and an optional purpose, never its facts |
| Tool result returned to the model | The execution's state and its reason, and in SUCCEEDED the result |
| Nothing built-in | The signed receipt, and the Proof-of-Mandate on the audit trail |
With Mandaitor in an OpenAI tool-calling loop, who performs the write?
- Mandaitor, with its own credential, after deciding on the record. Your handler only asks.
- Your handler, once Mandaitor answers ALLOW.
- The model, once its system prompt says it may.
Reveal answer
The model proposes, your handler asks, and Mandaitor decides on the record as the target system holds it, then performs the write itself. Neither the model nor your code holds a credential that could write without asking, so the mandate is the only way in.
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: a sandbox, an agent and its mandate, and a first execution.
- Executions: the full contract, from setup to receipts.
- MCP Server: connect an MCP client instead of writing a handler.
- Proof-of-Mandate and Receipt: what the receipt proves, and how to check it.
Save your learning progress
Mark this lesson as complete to update the Academy overview without requiring an account.