How to Integrate SafeClaw with OpenAI Assistants API
SafeClaw provides action-level gating for AI agents. This guide covers wrapping OpenAI Assistants API tool calls and function invocations with SafeClaw policy checks. Every action is evaluated against a deny-by-default policy before the assistant executes it.
Prerequisites
- Node.js 18 or later
- OpenAI API key with Assistants API access
openainpm package v4 or later- A SafeClaw account at safeclaw.onrender.com (free tier, 7-day renewable keys, no credit card)
- SafeClaw API key from the browser dashboard
Step-by-Step Instructions
Step 1: Install SafeClaw
npx @authensor/safeclaw
Complete the setup wizard in your browser. SafeClaw ships with zero third-party dependencies and runs in TypeScript strict mode.
Step 2: Install the OpenAI SDK
npm install openai
Step 3: Create the SafeClaw Middleware
Create safeclaw-openai-gate.ts:
import { SafeClaw } from "@authensor/safeclaw";
const safeclaw = new SafeClaw({
apiKey: process.env.SAFECLAW_API_KEY,
agentId: "openai-assistant",
mode: "enforce",
});
export async function gateToolCall(toolCall: {
function: { name: string; arguments: string };
}): Promise<{ allowed: boolean; decision: string; reason?: string }> {
const args = JSON.parse(toolCall.function.arguments);
const actionType = mapFunctionToActionType(toolCall.function.name);
const result = await safeclaw.evaluate({
actionType,
target: args.path || args.url || args.command || toolCall.function.name,
metadata: {
agent: "openai-assistant",
functionName: toolCall.function.name,
timestamp: new Date().toISOString(),
},
});
return {
allowed: result.decision === "ALLOW",
decision: result.decision,
reason: result.reason,
};
}
function mapFunctionToActionType(name: string): string {
const mapping: Record<string, string> = {
write_file: "file_write",
read_file: "file_read",
execute_code: "shell_exec",
run_command: "shell_exec",
fetch_url: "network",
api_call: "network",
};
return mapping[name] || "shell_exec";
}
Step 4: Wrap the Assistants API Run Loop
import OpenAI from "openai";
import { gateToolCall } from "./safeclaw-openai-gate";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function runAssistantWithSafeClaw(
threadId: string,
assistantId: string
) {
let run = await openai.beta.threads.runs.create(threadId, {
assistant_id: assistantId,
});
while (run.status === "requires_action") {
const toolCalls =
run.required_action?.submit_tool_outputs?.tool_calls || [];
const toolOutputs = [];
for (const toolCall of toolCalls) {
const gate = await gateToolCall(toolCall);
if (!gate.allowed) {
toolOutputs.push({
tool_call_id: toolCall.id,
output: JSON.stringify({
error: "Action blocked by SafeClaw policy",
decision: gate.decision,
reason: gate.reason,
}),
});
continue;
}
// Execute the tool call only if SafeClaw allows it
const result = await executeToolCall(toolCall);
toolOutputs.push({
tool_call_id: toolCall.id,
output: JSON.stringify(result),
});
}
run = await openai.beta.threads.runs.submitToolOutputs(
threadId,
run.id,
{ tool_outputs: toolOutputs }
);
}
return run;
}
Step 5: Enable Simulation Mode for Testing
Set mode: "simulate" in the SafeClaw constructor. All tool calls are logged to the tamper-proof audit trail (SHA-256 hash chain) but none are blocked. Review results in the dashboard.
Step 6: Switch to Enforce Mode
Set mode: "enforce" after confirming expected behavior. SafeClaw evaluates each action in sub-millisecond time.
Example Policy
# safeclaw.config.yaml
version: "1.0"
agent: openai-assistant
defaultAction: deny
rules:
- id: allow-read-docs
action: file_read
target: "./docs/**"
decision: allow
description: "Allow reading documentation files"
- id: allow-write-output
action: file_write
target: "./output/**"
decision: allow
description: "Allow writing to output directory"
- id: deny-write-config
action: file_write
target: ".config."
decision: deny
description: "Block writing to config files"
- id: gate-code-execution
action: shell_exec
target: "*"
decision: require_approval
description: "All code execution requires approval"
- id: allow-internal-api
action: network
target: "https://api.internal.company.com/**"
decision: allow
- id: deny-external-network
action: network
target: "*"
decision: deny
Example Action Requests
1. ALLOW — Reading a documentation file:
{
"actionType": "file_read",
"target": "./docs/api-reference.md",
"agentId": "openai-assistant",
"decision": "ALLOW",
"rule": "allow-read-docs",
"evaluationTime": "0.3ms"
}
2. DENY — Writing to a config file:
{
"actionType": "file_write",
"target": "webpack.config.js",
"agentId": "openai-assistant",
"decision": "DENY",
"rule": "deny-write-config",
"evaluationTime": "0.2ms"
}
3. REQUIRE_APPROVAL — Executing code via code interpreter:
{
"actionType": "shell_exec",
"target": "python3 analysis.py",
"agentId": "openai-assistant",
"decision": "REQUIRE_APPROVAL",
"rule": "gate-code-execution",
"evaluationTime": "0.4ms"
}
4. ALLOW — Calling an internal API:
{
"actionType": "network",
"target": "https://api.internal.company.com/users",
"agentId": "openai-assistant",
"decision": "ALLOW",
"rule": "allow-internal-api",
"evaluationTime": "0.3ms"
}
5. DENY — External network request blocked by default:
{
"actionType": "network",
"target": "https://random-external-service.com/data",
"agentId": "openai-assistant",
"decision": "DENY",
"rule": "deny-external-network",
"evaluationTime": "0.2ms"
}
Troubleshooting
Issue 1: Tool outputs rejected by OpenAI API
Symptom: submit_tool_outputs returns a 400 error.
Fix: Ensure every tool call in required_action.submit_tool_outputs.tool_calls receives a corresponding output, including denied actions. Return a JSON error message for denied calls instead of omitting them.
Issue 2: Function name not mapping to action type
Symptom: SafeClaw evaluates all actions as shell_exec regardless of function.
Fix: Update the mapFunctionToActionType function to include all custom function names defined in your assistant's tool definitions. Each function name must map to one of: file_write, file_read, shell_exec, network.
Issue 3: Audit logs not appearing in dashboard
Symptom: Actions execute but no entries appear at safeclaw.onrender.com.
Fix: Verify SAFECLAW_API_KEY is set correctly. The control plane receives only action metadata, never your keys or data. Check network connectivity to safeclaw.onrender.com. Ensure the SafeClaw client is initialized before the first tool call.
Cross-References
- SafeClaw Action Types Reference
- OpenAI Assistants vs Direct API: SafeClaw Differences
- Glossary: Deny-by-Default Architecture
- FAQ: Does SafeClaw Work with OpenAI Function Calling?
- Use Case: Securing OpenAI Assistants in Enterprise
Try SafeClaw
Action-level gating for AI agents. Set it up in your browser in 60 seconds.
$ npx @authensor/safeclaw