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 … (or X-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.

1

Go to the sign‑up page

Open Register (or “Register” in the site header).

2

Enter your email and password

Use a real email and a strong password.

3

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).

1

Open the dashboard

Click Dashboard in the header (or go to /admin).

2

Create or choose an organization

Create one with a name and slug, or use the organization switcher in the sidebar.

3

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.

1

Go to Agents

In the dashboard sidebar, open Agents.

2

Create a new agent

Click “New agent”, set a name (e.g. “Claude worker” or “Production API”) and optional slug, then save.

3

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.

1

Open API keys

In the sidebar, go to API keys (under the agent or organization, depending on your panel layout).

2

Create a key

Link it to the agent you created. Copy the key immediately.

3

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:

  • decisionallow, warn, review, or deny
  • 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/context exceeds 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.

1

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.

2

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.

3

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.

4

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() and gateAction() 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 ownersyes. 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

  1. Register at SafeNode and create an organization.
  2. Create an agent named Seedling with slug seedling.
  3. Create an API key on that agent. Store it server-side as SAFENODE_API_KEY.
  4. Policies → New policy → add a version with the starter JSON below → Set active.
  5. 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

  1. Register and create organization + agent + API key.
  2. Set SAFENODE_API_KEY in your environment.
  3. 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.