2026-01-19 · Authensor

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

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

Try SafeClaw

Action-level gating for AI agents. Set it up in your browser in 60 seconds.

$ npx @authensor/safeclaw