Documentation
Everything you need to run SafeNode in production, call the evaluate API, and gate actions from any agent, app, or automation you build.
Getting started
In a few minutes you will: create an account, set up an organization and an agent, create an API key, and call POST /api/v1/evaluate so your software can ask “is this action allowed?” before it runs.
Use the sidebar links to jump between sections. When you are logged into the dashboard, open Documentation from the Help block in the sidebar for the same guide.
What is SafeNode?
SafeNode is a policy firewall for AI agents. Before your agent performs an action (send email, run a shell command, call an external API, write a file, etc.), you send a structured description of that action. SafeNode runs it through your organization’s policies and returns a decision: allow, warn, review, or deny. Your integration chooses how strictly to enforce each outcome.
Why use it? You get one place for rules, a full audit trail of evaluations, and the ability to tighten or loosen behavior without redeploying every client.
Product site: https://safenode.tech
Integrations overview
SafeNode is agent-agnostic. Any software that can make an HTTPS request can call POST /api/v1/evaluate before performing a gated action.
- Custom agents
- Workers, chatbots, and tool-use loops (e.g. built with the Claude Agent SDK or your own orchestration). Gate each tool call or write. See Custom agents.
- Product backends
- Server-side enforcement before CRM writes, billing actions, or admin APIs. See Seedling CRM for one example.
- Scripts and automations
- Cron jobs, CI steps, or internal tools that need a policy check and audit trail. Use curl, the JavaScript client SDK, or any HTTP client.
Pattern everywhere: evaluate first, act second. Your code branches on decision and logs trace_id.
Core concepts
- Organization
- Top-level tenant. Policies, agents, API keys, and evaluations belong to one organization.
- Agent
- A logical actor (e.g. “Support bot”, “Workflow worker”, a production service). API keys are tied to an agent so you can filter and audit by source.
- API key
- Secret used in
Authorization: Bearer …(orX-Api-Key). Identifies the agent and organization for each evaluate call. action_type- Short string naming the kind of action (e.g.
tool.shell.exec,send_email). Your policies can match on this. payload- JSON object with action-specific details (arguments, paths, recipients, model name, etc.).
context- JSON object with environment metadata (region, estimated cost, sensitivity, workspace id). Helps scoring and rules.
trace_id- Returned on every successful evaluation. Use it to correlate logs with a row in the Decision Feed.
Create your account
You need a SafeNode account to create organizations, agents, and API keys.
Go to the sign‑up page
Open Register (or “Register” in the site header).
Enter your email and password
Use a real email and a strong password.
Log in
After registering, log in via Log in when needed.
Create an organization
Everything in SafeNode lives under an organization. You can have more than one (e.g. staging vs production).
Open the dashboard
Click Dashboard in the header (or go to /admin).
Create or choose an organization
Create one with a name and slug, or use the organization switcher in the sidebar.
Remember the slug
The slug (e.g. acme) is a short identifier you may reference in context from your apps.
Add an agent
An agent is one thing that can perform actions. Create separate agents per integration or environment so evaluations stay attributable.
Go to Agents
In the dashboard sidebar, open Agents.
Create a new agent
Click “New agent”, set a name (e.g. “Claude worker” or “Production API”) and optional slug, then save.
One agent per logical actor
Prefer one agent per integration or environment rather than sharing one key everywhere.
Get your API key
Programs authenticate with an API key tied to an agent. The full key is shown only once when created—store it in a secret manager or environment variable.
Open API keys
In the sidebar, go to API keys (under the agent or organization, depending on your panel layout).
Create a key
Link it to the agent you created. Copy the key immediately.
Send it on every evaluate request
Use Authorization: Bearer YOUR_KEY or X-Api-Key: YOUR_KEY. Missing or invalid keys receive 401.
Call the API
Main endpoint: POST /api/v1/evaluate. Full URL is your app base URL plus /api/v1/evaluate (from APP_URL), e.g. https://safenode.tech/api/v1/evaluate.
Headers
POST /api/v1/evaluate
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
Body (required fields)
action_type(string, required)payload(object, optional—omit or send{})context(object, optional)agent_id(string, optional—usually omitted when the API key already identifies one agent)
Minimal example
{
"action_type": "send_email",
"payload": {
"to": "user@example.com",
"subject": "Your order",
"body": "Order #123 has shipped."
}
}
With context (recommended)
{
"action_type": "call_model",
"payload": { "model": "gpt-4", "tokens": 500 },
"context": {
"region": "eu-west-1",
"cost_usd": 0.02,
"sensitivity": "internal"
}
}
Align context field names with what your policies expect.
Understand the response
On success, JSON includes:
- decision —
allow,warn,review, ordeny - trace_id — unique id for this evaluation
- impact_score / risk_score — numeric scores from policy scoring
- matched_policies — rules that matched
- reasons — human-readable strings
- alternatives — suggested safer options when applicable
Suggested enforcement:
deny— do not perform the action.review— block until a human approves (or your product policy allows auto-escalation).warn— allow only if your product policy says warnings are acceptable; log and surface to the user.allow— proceed.
{
"decision": "allow",
"trace_id": "ev_abc123",
"impact_score": 0.2,
"risk_score": 0.1,
"matched_policies": [],
"reasons": [],
"alternatives": []
}
Rate limits & errors
The evaluate endpoint is rate limited per API key (and organization) to protect the service. Defaults are configurable on the server; typical values are on the order of tens to low hundreds of requests per minute per key. If you exceed the limit, you may receive 429 Too Many Requests.
Other common responses:
- 401 — missing/invalid API key or expired key.
- 422 — request validation failed or JSON-encoded
payload/contextexceeds the configured maximum size. - 500 — server error (rare); check service status and logs.
Implement retries with backoff for 429 and transient 5xx. For high-risk actions, prefer fail-closed if SafeNode is unreachable unless you explicitly allow read-only fail-open behavior.
Using the dashboard
- Decision Feed — recent evaluations; filter by decision, agent, or time; open a row for full detail and
trace_id. - Stats — volume and outcome breakdowns.
- Policies — active policy version, hard rules, soft rules, weights, and decision bands.
- Overrides — manual allow/deny on past evaluations (audited).
For a short marketing overview of the product, you can still read How it works on the homepage—this documentation is the operational source of truth.
Custom agents
Whether you use the Claude Agent SDK, LangChain, a hand-rolled tool loop, or a product-specific worker, the integration shape is the same: before a tool runs or a write is persisted, call SafeNode and honor the decision.
Agent + API key in SafeNode
Create one agent per integration or environment. Store SAFENODE_API_KEY and SAFENODE_BASE_URL server-side — never in client-side code or a public repo.
Hook the tool / action boundary
In your agent runtime, intercept each tool call (or each external write). Map the tool name to an action_type, build payload and context, then call evaluate.
Use stable action_type names
Pick a namespace per agent, e.g. tool.send_email, tool.shell.exec, tool.fs.write, so policies can target one integration without affecting others.
Enforce the decision
deny and (usually) review block the tool. warn is your product choice. Log trace_id on every path.
Example: Claude Agent SDK tool hook
Illustrative pattern — adapt to your SDK version and tool registry. The important part is gating before side effects:
import { SafeNodeClient } from "./scripts/safenode-client-sdk.mjs";
const safenode = new SafeNodeClient({
baseUrl: process.env.SAFENODE_BASE_URL,
apiKey: process.env.SAFENODE_API_KEY,
});
async function runToolWithPolicy(toolName, toolInput, sessionContext) {
const gate = await safenode.gateAction(
{
actionType: `tool.${toolName}`,
payload: {
summary: `Run tool: ${toolName}`,
fields: toolInput,
},
context: {
source: "claude-agent",
user_id: sessionContext.userId,
session_id: sessionContext.sessionId,
},
},
{ allowWarn: false, allowReview: false }
);
if (!gate.allowed) {
return {
type: "tool_result",
content: `Blocked by policy: ${gate.reason} (trace ${gate.traceId})`,
is_error: true,
};
}
return executeToolImpl(toolName, toolInput);
}
Attach runToolWithPolicy wherever your agent dispatches tool calls. For read-only tools you may choose fail-open when SafeNode is down; for writes, prefer fail-closed.
Optional helper: the JavaScript client SDK in this repo (scripts/safenode-client-sdk.mjs) wraps evaluate with timeouts, retries, and gateAction() defaults.
JavaScript client SDK
Small reference ES module in the SafeNode repo (no npm package required to start):
- Path:
scripts/safenode-client-sdk.mjs - Exports:
SafeNodeClient,gateWithSafeNode - Features: Bearer auth, request timeout, exponential backoff retries on transient failures,
evaluate()andgateAction()with fail-closed defaults and optional read-only fail-open.
import { SafeNodeClient } from "./scripts/safenode-client-sdk.mjs";
const client = new SafeNodeClient({
baseUrl: process.env.SAFENODE_BASE_URL,
apiKey: process.env.SAFENODE_API_KEY,
timeoutMs: 5000,
maxRetries: 2,
});
const gate = await client.gateAction(
{
actionType: "tool.send_email",
payload: { to: "user@example.com", subject: "Hello" },
context: { environment: "production", sensitivity: "internal" },
},
{ allowWarn: true, allowReview: false, failOpenForReadOnly: true, isReadOnlyAction: false }
);
if (!gate.allowed) {
// block action; gate.reason, gate.traceId, gate.response
}
Copy the file into your agent project or vendor it from this repository.
Seedling CRM integration
Seedling CRM can call SafeNode before gated writes (tasks, customers, calendar, etc.). If you connect an external agent to Seedling (see Seedling’s API help), you need your own SafeNode organization, agent, API key, and policy — Seedling does not configure that for you.
Who needs a SafeNode policy?
- Seedling app users only — usually no. The Seedling platform runs server-side enforcement with its own API key and policy. Your writes are gated by that platform policy.
- Agent / automation owners — yes. When you connect Cursor, a custom agent, or a worker to Seedling, create SafeNode credentials and a policy on your SafeNode org, then paste the API key into Seedling’s agent settings.
Your API key determines which SafeNode organization enforces policy. Do not set a separate org id env var — only SAFENODE_BASE_URL and SAFENODE_API_KEY.
Setup checklist
- Register at SafeNode and create an organization.
- Create an agent named Seedling with slug
seedling. - Create an API key on that agent. Store it server-side as
SAFENODE_API_KEY. - Policies → New policy → add a version with the starter JSON below → Set active.
- In Seedling, paste the key where agent / API integration settings ask for SafeNode credentials.
Starter policy (Seedling writes)
Denies destructive delete_* actions, routes large bulk task creates to review, and optionally warns on off-hours customer creation. Tune timezones and thresholds for your org.
{
"version": 1,
"hard_rules": [
{
"id": "deny-deletes",
"type": "action_type_match",
"params": {
"patterns": [
"delete_*"
],
"message": "Destructive delete actions are denied by default."
}
}
],
"soft_rules": [
{
"id": "bulk-task-review",
"type": "action_type_context_threshold",
"params": {
"action_types": [
"bulk_create_task"
],
"context_key": "count",
"gt": 5,
"message": "Bulk task creation exceeds 5 items \u2014 route to human review."
},
"weight": 100
},
{
"id": "customer-business-hours",
"type": "action_type_business_hours",
"params": {
"action_types": [
"create_customer"
],
"timezone": "America/Los_Angeles",
"start_hour": 9,
"end_hour": 17,
"weekdays_only": true,
"message": "Creating customers outside business hours should be reviewed."
},
"weight": 50
}
],
"weights": {
"privacy": 25,
"carbon": 15,
"cost": 20,
"trust": 20,
"policy_fit": 20
},
"default_decision": "allow",
"decision_bands": {
"allow_max": 30,
"warn_max": 50,
"review_max": 75
}
}
Rule types used: action_type_match, action_type_context_threshold, action_type_business_hours. For allowlists per Seedling tenant inside one policy, add context_match on context.org_id (the Seedling tenant id — not your SafeNode org id).
Action catalog (source of truth for action_type strings): Seedling repo docs/seedling-api/safenode-action-catalog.md.
Full example
- Register and create organization + agent + API key.
- Set
SAFENODE_API_KEYin your environment. - Run:
curl -X POST https://safenode.tech/api/v1/evaluate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SAFENODE_API_KEY" \
-d '{
"action_type": "send_email",
"payload": { "to": "user@example.com", "subject": "Hi", "body": "Hello" },
"context": { "region": "us-east-1" }
}'
Inspect decision and trace_id, then find the evaluation in the Decision Feed.
Site admin (production)
Site admins can access the separate Super Admin panel at /super-admin (manage users, site-wide settings). This is independent of organization owner / admin roles inside the main dashboard.
On the server, SSH to the app directory and run:
php artisan safenode:make-site-admin your@email.com
If the user does not exist yet:
php artisan safenode:make-site-admin your@email.com --create
The command will prompt for name, password, and optionally an organization slug. Afterward that user can open /super-admin when logged in.