Xybern API Documentation

The Authorisation API for AI agents. Intercept, evaluate, and enforce every agent action before it runs.

ℹ️

Base URL: https://www.xybern.com/api/v1

The Xybern Authorisation API sits between your AI agents and the world. Every tool call, every delegation, every action — evaluated against your policies and either allowed or denied in real time. Full audit trail included.

Authentication

All API requests require your API key in the X-API-Key header.

X-API-Key: xb_your_api_key_here

Get your API key from the Sentinel Dashboard under API Keys.

⚠️

Keep your API key secure. Never expose it in client-side code or public repositories.

SDK Installation New

Download our official SDKs for Python and JavaScript - no package manager required.

📦

How it works: Download the SDK file to your project folder, then import it. No pip/npm install needed.

Python SDK

Step 1: Install the required dependency

pip install requests

Step 2: Download the SDK to your project folder

# Navigate to your project directory first
cd /path/to/your/project

# Download the SDK file
curl -O https://www.xybern.com/static/sdk/xybern.py

# Now you have xybern.py in your project folder

Step 3: Import and use in your Python script (must be in same folder)

# your_script.py (in the same folder as xybern.py)
from xybern import Xybern

client = Xybern(api_key="xb_your_api_key")

result = client.verify(
    content="AI generated text to verify...",
    source={"type": "llm", "model": "gpt-4", "provider": "openai"}
)

print(f"Trust Score: {result['trust_score']}%")
print(f"Fairness Score: {result['bias']['fairness_score']}%")

Project structure:

my_project/
├── xybern.py          # Downloaded SDK
├── your_script.py     # Your code that imports xybern
└── ...

JavaScript SDK

Option A: Browser (easiest)

Just add a script tag - no download needed:

<!DOCTYPE html>
<html>
<head>
    <script src="https://www.xybern.com/static/sdk/xybern.js"></script>
</head>
<body>
    <script>
        const client = new Xybern({ apiKey: 'xb_your_api_key' });
        
        async function verify() {
            const result = await client.verify({
                content: 'AI generated text...',
                source: { type: 'llm', model: 'gpt-4' }
            });
            console.log('Trust Score:', result.trust_score);
        }
        
        verify();
    </script>
</body>
</html>

Option B: Node.js

Step 1: Download the SDK to your project folder

# Navigate to your project directory
cd /path/to/your/project

# Download the SDK file
curl -O https://www.xybern.com/static/sdk/xybern.js

Step 2: Require and use in your script

// app.js (in the same folder as xybern.js)
const { Xybern } = require('./xybern.js');

const client = new Xybern({ apiKey: 'xb_your_api_key' });

async function main() {
    const result = await client.verify({
        content: 'AI generated text...',
        source: { type: 'llm', model: 'gpt-4' }
    });
    
    console.log(`Trust Score: ${result.trust_score}%`);
}

main();

Project structure:

my_project/
├── xybern.js          # Downloaded SDK
├── app.js             # Your code that requires xybern
└── package.json

That's it! The SDK file must be in the same directory as your script. No global installation needed.

⚠️

Important: Keep your API key secure. For production, use environment variables instead of hardcoding.

Quick Start (Direct API)

Or use the API directly without the SDK:

import requests

API_KEY = "xb_your_api_key_here"

response = requests.post(
    "https://www.xybern.com/api/v1/enforce/intercept",
    headers={"X-API-Key": API_KEY},
    json={
        "agent_id": "agent_crewai_001",
        "action": {
            "type": "tool_call",
            "tool": "send_email",
            "parameters": {"to": "cfo@company.com", "subject": "Q4 Report"}
        },
        "context": {"task": "financial_reporting", "session_id": "sess_abc123"}
    }
)

result = response.json()
print(f"Decision: {result['decision']}")   # ALLOW or DENY
print(f"Reason: {result['reason']}")
const API_KEY = "xb_your_api_key_here";

const response = await fetch("https://www.xybern.com/api/v1/enforce/intercept", {
    method: "POST",
    headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({
        agent_id: "agent_autogen_001",
        action: {
            type: "tool_call",
            tool: "write_file",
            parameters: { path: "/reports/q4.csv", content: "..." }
        },
        context: { task: "report_generation", session_id: "sess_xyz789" }
    })
});

const result = await response.json();
console.log(`Decision: ${result.decision}`);  // ALLOW or DENY
curl -X POST https://www.xybern.com/api/v1/enforce/intercept \
  -H "X-API-Key: xb_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_langgraph_001",
    "action": {
      "type": "tool_call",
      "tool": "query_database",
      "parameters": {"table": "customers", "query": "SELECT * FROM customers"}
    },
    "context": {"task": "data_analysis", "session_id": "sess_lan001"}
  }'

Authorisation New

Pre-execution AI governance layer that intercepts every AI action before it reaches any real system. Enforces policies, manages agent trust, and records immutable audit trails.

ℹ️
How it works: Your AI agents call /v1/enforce/intercept before executing any action. The control plane evaluates policies, checks agent trust, and returns allow, block, or escalate. Every decision is recorded in the Sentinel Vault hash chain.

Architecture

Agent wants to execute_trade
    ↓
POST /v1/enforce/intercept  (+ optional signed_assertion for identity)
    ↓
┌──────────────────────────────────────┐
│  Identity Verification  (< 1ms)       │  ← Ed25519 sig + Redis nonce dedup
└──────────────────────────────────────┘
    ↓
┌──────────────────────────────────────┐
│  Policy Engine                        │  ← All active policies evaluated
│  • action_type  — name matching       │
│  • threshold    — trust level         │
│  • content      — regex patterns      │
│  • temporal     — hours / days        │
│  • chain        — delegation depth    │
│  • identity     — DID / scope rules   │
│  • metadata     — field comparisons   │  ← notional_usd > 100k, ticker == X, …
│  • composite    — AND / OR combiner   │
└──────────────────────────────────────┘
    ↓
┌──────────────────────────────────────┐
│  Decision                             │
│  allow    → fast path (~5ms)          │
│  block    → fast path (~5ms)          │
│  escalate → human review queue        │
└──────────────────────────────────────┘
    ↓
Vault entry + decision returned to SDK / caller

Decision Paths

PathLatencyDescription
fast~5msPolicy directly blocks or allows — no LLM call
standard3-5sFull LLM verification runs against the action content
escalationAction held for human review in the escalation queue

POST /v1/enforce/intercept Core

Intercept an AI action before execution. This is the primary endpoint — every action must pass through here.

POST /v1/enforce/intercept

Pre-execution interception with policy evaluation, trust scoring, and vault recording.

Request Body

FieldTypeRequiredDescription
action_typestringYesAction name: execute_trade, send_email, query_database, etc.
action_contentstringNoContent being actioned (e.g. email body, SQL query)
metadataobjectNoArbitrary metadata (amount, recipient, etc.)
agent_idstringNoRegistered agent performing the action
chain_idstringNoMulti-step chain identifier
chain_stepintegerNoStep number within the chain
parent_decision_idstringNoPrevious decision in this chain

Example

import requests

API_KEY = "xb_your_api_key"

# Before executing a trade, intercept it
result = requests.post(
    "https://www.xybern.com/api/v1/enforce/intercept",
    headers={"X-API-Key": API_KEY},
    json={
        "action_type": "execute_trade",
        "action_content": "Buy 1000 shares of AAPL at market price",
        "metadata": {"symbol": "AAPL", "quantity": 1000, "side": "buy"},
        "agent_id": "agent_abc123"
    }
).json()

if result["decision"] == "allow":
    execute_trade()  # proceed
elif result["decision"] == "block":
    log_blocked(result["reasoning"])
elif result["decision"] == "escalate":
    notify_human(result["decision_id"])

Response

{
  "ok": true,
  "decision": "allow",
  "decision_id": "enf_7c8f8c26e62c",
  "decision_path": "fast",
  "trust_score": 85,
  "reasoning": "No policies triggered — default allow",
  "policies_evaluated": ["policy_123", "policy_456"],
  "policies_triggered": [],
  "vault_entry_id": "ve_abc123",
  "latency_ms": 5,
  "created_at": "2026-03-13T21:48:54Z"
}

POST /v1/enforce/agent-comm

Intercept agent-to-agent instructions. When Agent A tells Agent B to do something, the instruction flows through the control plane first.

POST /v1/enforce/agent-comm

Agent-to-agent communication interception with full audit trail.

Request Body

FieldTypeRequiredDescription
source_agent_idstringYesAgent sending the instruction
target_agent_idstringYesAgent receiving the instruction
instructionstringYesThe instruction content
action_typestringNoDefaults to agent_instruction
chain_idstringNoChain identifier for multi-step flows

Example

result = requests.post(
    "https://www.xybern.com/api/v1/enforce/agent-comm",
    headers={"X-API-Key": API_KEY},
    json={
        "source_agent_id": "agent_research_01",
        "target_agent_id": "agent_trading_01",
        "instruction": "Execute buy order for AAPL based on analysis",
        "chain_id": "chain_research_trade_001"
    }
).json()

if result["decision"] == "allow":
    forward_instruction_to_target_agent()

Enforcement Policies

Create, read, update, and delete enforcement policies programmatically. Policies are evaluated in priority order (highest first) and the most restrictive decision wins.

GET /v1/enforce/policies

List all policies for the workspace, ordered by priority.

POST /v1/enforce/policies

Create a new enforcement policy.

PUT /v1/enforce/policies/{id}

Update an existing policy.

DELETE /v1/enforce/policies/{id}

Delete a policy.

Policy Types

TypeWhat It DoesConditions
action_typeMatches actions by nameUses action_types field (supports wildcards: delete_*)
thresholdBlocks agents below a trust levelUses trust_threshold field
content_patternRegex match on action contentconditions.patterns: array of regex strings
temporalTime/day restrictionsconditions.blocked_hours: [0-23], conditions.blocked_days: [1-7]
chain_of_custodyAgent chain depth/agent rulesconditions.max_chain_depth, conditions.forbidden_agents

Create Policy Example

# Block trades containing sensitive keywords
policy = requests.post(
    "https://www.xybern.com/api/v1/enforce/policies",
    headers={"X-API-Key": API_KEY},
    json={
        "name": "Block Insider Trading Keywords",
        "description": "Block any trade with insider-related language",
        "policy_type": "content_pattern",
        "decision": "block",
        "priority": 200,
        "action_types": ["execute_trade", "modify_order"],
        "conditions": {
            "patterns": [
                "insider.*info",
                "material.*non-public",
                "tip.*from.*executive"
            ]
        }
    }
).json()

# Block all activity on weekends
weekend_policy = requests.post(
    "https://www.xybern.com/api/v1/enforce/policies",
    headers={"X-API-Key": API_KEY},
    json={
        "name": "Weekend Trading Lockout",
        "policy_type": "temporal",
        "decision": "block",
        "priority": 300,
        "action_types": ["execute_trade"],
        "conditions": {"blocked_days": [6, 7]}
    }
).json()

Agent Registry

Register AI agents with the control plane. Each agent accumulates an adaptive trust level — clean records earn fast-path clearance, blocks reduce trust.

POST /v1/enforce/agents

Register a new agent.

GET /v1/enforce/agents

List all registered agents.

GET /v1/enforce/agents/{agent_id}

Get agent details including trust level and action history.

GET /v1/enforce/agents/{agent_id}/history

Get trust history and decision timeline for an agent.

Adaptive Trust

EventTrust ChangeDescription
Action allowed+0.2Clean record builds trust over time
Action blocked-2.0Policy violations significantly reduce trust
Action escalated-0.5Uncertain decisions slightly reduce trust

Register Agent Example

# Register a trading agent
agent = requests.post(
    "https://www.xybern.com/api/v1/enforce/agents",
    headers={"X-API-Key": API_KEY},
    json={
        "name": "Trading Agent Alpha",
        "framework": "langchain",
        "description": "Automated equity trading agent",
        "capabilities": ["execute_trade", "read_data", "send_email"],
        "permissions": {
            "allowed_action_types": ["execute_trade", "read_data"],
            "denied_action_types": ["delete_data", "admin_action"]
        }
    }
).json()

agent_id = agent["agent"]["agent_id"]
print(f"Registered: {agent_id}, trust: {agent['agent']['trust_level']}")

Decisions & Escalations

Query the immutable decision log and manage the human review queue.

GET /v1/enforce/decisions

List decisions with pagination and filtering by action_type or decision.

GET /v1/enforce/decisions/{decision_id}

Get a single decision with full details.

GET /v1/enforce/escalations

List pending escalations (actions awaiting human review).

POST /v1/enforce/escalations/{id}/resolve

Approve or reject an escalated action. Body: {"resolution": "approved"|"rejected"}. Immediately unblocks any SDK call polling this escalation.

GET /v1/enforce/escalations/{id}/status

Poll the resolution status of a pending escalation. Returns {"ok": true, "status": "pending"|"approved"|"rejected"}. Called automatically by the SDK's wait_for_escalation() method every escalation_poll_interval seconds.

Query Decisions

# Get all blocked decisions
blocked = requests.get(
    "https://www.xybern.com/api/v1/enforce/decisions",
    headers={"X-API-Key": API_KEY},
    params={"decision": "block", "per_page": 20}
).json()

for d in blocked["decisions"]:
    print(f"{d['decision_id']}: {d['action_type']} → {d['decision']} "
          f"(trust: {d['trust_score']}, {d['latency_ms']}ms)")

# Resolve an escalation
requests.post(
    f"https://www.xybern.com/api/v1/enforce/escalations/{esc_id}/resolve",
    headers={"X-API-Key": API_KEY},
    json={"resolution": "approved", "reason": "Reviewed and safe to proceed"}
)

Stats Endpoint

GET /v1/enforce/stats

Aggregate enforcement statistics: decisions, block rate, avg latency, agent counts.

Custom Policy Builder

Build enforcement policies tailored to your organization's needs. The policy engine supports six built-in types and a composite type for complex AND/OR logic. All types are available in the Sentinel Dashboard UI under Control Plane → Add Policy.

Content Pattern Policy

Block or escalate actions whose content matches regex patterns. Useful for PII detection, insider trading keywords, or prohibited content.

{
    "name": "PII Detection",
    "policy_type": "content_pattern",
    "decision": "escalate",
    "action_types": ["send_email", "export_data"],
    "conditions": {
        "patterns": [
            "\\b\\d{3}-\\d{2}-\\d{4}\\b",
            "\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b",
            "password|secret|credential|api[_-]?key"
        ]
    }
}

Temporal Policy

Restrict actions to specific hours or days. Blocked hours are in UTC (0-23), blocked days use ISO format (1=Monday, 7=Sunday).

{
    "name": "After-Hours Lockout",
    "policy_type": "temporal",
    "decision": "block",
    "action_types": ["execute_trade", "wire_transfer"],
    "conditions": {
        "blocked_hours": [0, 1, 2, 3, 4, 5, 22, 23],
        "blocked_days": [6, 7]
    }
}

Chain of Custody Policy

Limit agent delegation depth and block specific agents from chains.

{
    "name": "Max Delegation Depth",
    "policy_type": "chain_of_custody",
    "decision": "escalate",
    "conditions": {
        "max_chain_depth": 3,
        "forbidden_agents": ["agent_untrusted_001"],
        "required_agents": ["agent_compliance_reviewer"]
    }
}

Dashboard support: All policy types can be created from the Sentinel Dashboard UI under Control Plane → Add Policy, with dynamic condition fields that appear based on the selected type.

Metadata Field Policy New

Evaluate field-level conditions against the metadata object passed on each intercept call. Combine rules with AND/OR logic to build precise, data-driven policies — such as blocking high-notional trades or flagging specific tickers.

ℹ️
How it works: When you call /enforce/intercept with a metadata object, the engine evaluates every active metadata policy against those fields. Rules can reference any key in the metadata object with numeric or string comparisons.

Supported Operators

OperatorTypeExample
> < >= <=Numericnotional_usd > 100000
== !=String / Numericticker == "TSLA"
contains not_containsStringstrategy contains "pre-earnings"
exists not_existsPresenceinsider_flag exists

Example: Block High-Risk Financial Transactions

requests.post(f"{BASE}/enforce/policies", headers=HEADERS, json={
    "name": "High-Risk Financial Transactions",
    "policy_type": "metadata",
    "decision": "block",
    "action_types": ["execute_trade", "wire_transfer", "fund_transfer"],
    "conditions": {
        "operator": "AND",          # ALL rules must match to trigger
        "rules": [
            {"field": "notional_usd", "operator": ">",       "value": 100000},
            {"field": "strategy",     "operator": "contains", "value": "pre-earnings"}
        ]
    }
})

Example: OR Logic — Flag Any Large or Sensitive Action

{
    "name": "Sensitive Trade Escalation",
    "policy_type": "metadata",
    "decision": "escalate",
    "conditions": {
        "operator": "OR",           # ANY rule matching triggers the policy
        "rules": [
            {"field": "notional_usd", "operator": ">=", "value": 500000},
            {"field": "ticker",       "operator": "==", "value": "GME"},
            {"field": "insider_flag", "operator": "exists"}
        ]
    }
}

Intercept Call with Metadata

result = requests.post(f"{BASE}/enforce/intercept", headers=HEADERS, json={
    "action_type": "execute_trade",
    "action_content": "Buy $4.2M block of TSLA ahead of earnings",
    "agent_id": "agent_trading_01",
    "metadata": {
        "ticker":       "TSLA",
        "notional_usd": 4200000,
        "strategy":     "pre-earnings",
        "order_type":   "market"
    }
}).json()

# → decision: "block"
# → reasoning: "All metadata conditions met [metadata.notional_usd > 100000; ...]"
💡

SDK auto-extract: When using the @guard decorator or XybernToolkit, numeric and string keyword arguments are automatically extracted as metadata and evaluated against metadata policies — no manual metadata construction needed.

Okta for AI Agents — Cryptographic Identity v1.22

Every AI agent is assigned an Ed25519 cryptographic identity — a verifiable credential that defines what the agent is, what it's authorised to do, and under what conditions. The control plane verifies identity on every action before any policy evaluation. Replay protection is enforced via Redis-backed nonce deduplication across all workers.

Architecture: Ed25519 keypairs → W3C DIDs → Signed assertions → Redis replay guard → Permission boundaries → Credential lifecycle → Vault-bound proofs

Key Concepts

ConceptDescription
Ed25519 KeypairEvery agent gets a public/private key. Private key returned once at registration, never stored server-side.
DIDW3C-aligned did:xybern:{workspace}:{agent} identifier.
Signed AssertionJSON payload signed by agent's private key, verified by control plane in < 1 ms.
Permission BoundaryScopes, resource rules, temporal windows, delegation controls, rate limits.
Nonce + TimestampReplay protection: each assertion must have a unique nonce and timestamp within 5 minutes. Nonces are deduplicated in Redis across all API workers.

Register Agent (via SDK — recommended)

from xybern import Xybern, AgentCredential

client = Xybern(api_key="xb_your_key")

# Generates Ed25519 keypair, registers agent, issues credential — one call
cred = client.agents.register(
    name="Trading Agent Alpha",
    framework="langchain",
    capabilities=["execute_trade", "query_portfolio"],
)
cred.save("./trading_agent.cred")  # chmod 600, private key never leaves your server
print(cred.did)                    # did:xybern:{workspace}:agent_abc123
print(cred.key_fingerprint)        # SHA-256 hex fingerprint

Register Agent (raw API)

resp = requests.post(f"{BASE}/enforce/agents", headers=HEADERS, json={
    "name": "Trading Agent Alpha",
    "framework": "langchain",
    "scopes": ["trade:write", "db:read"],
    "permission_boundary": {
        "resource_rules": [{"action_types": ["execute_trade"], "max_amount": 50000}],
        "temporal": {"allowed_hours": {"start": 9, "end": 17}},
        "rate_limit": {"max_actions": 100, "window_minutes": 60}
    }
})
cred = resp.json()["credential"]
private_key = cred["private_key"]  # SAVE THIS — shown only once
did = cred["did"]                   # did:xybern:ws1:agent_abc123

Intercept with Identity (SDK — auto-signing)

# SDK signs every assertion automatically — no crypto code needed
result = client.agents.intercept(
    action_type="execute_trade",
    action_content="Buy 100 AAPL at market",
    credential="./trading_agent.cred",   # or AgentCredential object
    metadata={"ticker": "AAPL", "notional_usd": 19000},
)
# response includes identity_verified=True when signature checks pass
print(result.identity_verified)  # True

Intercept with Identity (raw API)

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from base64 import b64encode, b64decode
import json, uuid

nonce = uuid.uuid4().hex
payload = {"agent_id": "agent_abc", "action": "execute_trade",
           "nonce": nonce, "timestamp": "2026-01-25T12:00:00Z"}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()
pk = Ed25519PrivateKey.from_private_bytes(b64decode(PRIVATE_KEY))
sig = b64encode(pk.sign(canonical)).decode()

resp = requests.post(f"{BASE}/enforce/intercept", headers=HEADERS, json={
    "action_type": "execute_trade",
    "agent_id": "agent_abc",
    "signed_assertion": payload,
    "assertion_signature": sig
})
# → response: {"identity_verified": true, "identity": {"did": "...", "fingerprint": "..."}}

SDK Auto-Capture v1.19

Integrate once — every AI agent action is automatically captured on the Sentinel dashboard. No manual intercept() call per action. Three patterns available depending on your stack.

Option 1: @guard Decorator

Wrap any Python function. Every call is intercepted, signed, and recorded. Blocked actions raise AgentBlockedError and the wrapped function never executes. When an action is escalated and wait_on_escalate=True (default), the agent blocks until a human approves or rejects on the Sentinel dashboard.

from xybern import Xybern, AgentCredential, AgentBlockedError

client = Xybern(api_key="xb_your_key")
cred   = AgentCredential.load("./trading_agent.cred")

@client.agents.guard(credential=cred)
def execute_trade(ticker, quantity, notional_usd=0):
    place_order(ticker, quantity)   # only runs if Sentinel allows it

@client.agents.guard(credential=cred)
def query_portfolio(account):
    return get_holdings(account)

# Every call → captured on Sentinel dashboard, identity verified
execute_trade("AAPL", 100, notional_usd=19000)   # → ALLOW
execute_trade("TSLA", 1000, notional_usd=5000000) # → BLOCK (raises AgentBlockedError)

The @guard decorator accepts the following optional parameters for human-in-the-loop escalation gating:

ParameterDefaultDescription
credentialrequiredPath to .cred file or AgentCredential object
wait_on_escalateTrueBlock execution until a human approves or rejects on the dashboard
escalation_timeout300Maximum seconds to wait for a human decision before raising AgentBlockedError
escalation_poll_interval5Seconds between each poll of the escalation status endpoint
metadata_fnNoneCallable (args, kwargs) → dict for fine-grained policy metadata

Provide a metadata_fn for fine-grained policy evaluation:

@client.agents.guard(
    credential=cred,
    metadata_fn=lambda args, kw: {
        "ticker":       kw.get("ticker"),
        "notional_usd": kw.get("notional_usd", 0),
        "strategy":     kw.get("strategy", ""),
    }
)
def execute_trade(ticker, quantity, notional_usd=0, strategy=""):
    place_order(ticker, quantity)

Option 2: LangChain Callback Handler

Zero changes to tool code. Attach XybernLangChainCallback to any LangChain agent — every tool call is intercepted before execution.

from xybern import Xybern, XybernLangChainCallback, AgentCredential
from langchain.agents import initialize_agent

client  = Xybern(api_key="xb_your_key")
cred    = AgentCredential.load("./trading_agent.cred")
handler = XybernLangChainCallback(client=client, credential=cred)

agent = initialize_agent(tools, llm, callbacks=[handler])
# Done — every tool call appears on the Sentinel dashboard

Option 3: XybernToolkit

Wrap a list of plain callables for non-LangChain agents. Exposes tools as attributes with full auto-interception.

from xybern import Xybern, XybernToolkit, AgentCredential

client = Xybern(api_key="xb_your_key")
cred   = AgentCredential.load("./trading_agent.cred")

def execute_trade(ticker, quantity, notional_usd=0): ...
def query_portfolio(account): ...
def risk_analysis(sector): ...

tools = XybernToolkit(client=client, credential=cred,
                      tools=[execute_trade, query_portfolio, risk_analysis])

# All calls auto-captured — same API as the original functions
tools.execute_trade("AAPL", 100, notional_usd=19000)
tools.query_portfolio("ACC-001")
print(tools.list_tools())  # ["execute_trade", "query_portfolio", "risk_analysis"]

AgentCredential — Key Management

MethodDescription
cred.save(path)Write JSON credential file, chmod 600
AgentCredential.load(path)Load credential from file
AgentCredential.from_env("PREFIX")Load from env vars: PREFIX_AGENT_ID, PREFIX_PRIVATE_KEY, etc.
cred.to_env_snippet("PREFIX")Generate export lines for secrets managers / CI
client.agents.rotate_credential(agent_id)Rotate keypair — old key immediately invalid

Every decision is live on the dashboard. Whether via @guard, XybernLangChainCallback, or direct intercept(), every enforcement decision is recorded in real time under Sentinel → Control Plane → Enforcement Decisions with full metadata, trust score, and policy reasoning.

Credential Lifecycle

MethodEndpointDescription
GET/v1/enforce/agents/{id}/credentialsList all credentials for an agent
GET/v1/enforce/agents/{id}/credentials/activeGet active credential
POST/v1/enforce/agents/{id}/credentials/rotateRotate: revoke old, issue new (returns private key)
POST/v1/enforce/credentials/{id}/revokePermanently revoke a credential
GET/v1/enforce/credentialsList all workspace credentials
POST/v1/enforce/credentials/{id}/verifyVerify a signed assertion

Identity Policy Type

requests.post(f"{BASE}/enforce/policies", headers=HEADERS, json={
    "name": "Require Agent Identity",
    "policy_type": "identity",
    "decision": "block",
    "conditions": {
        "require_identity": True,
        "required_scopes": ["trade:write"],
        "blocked_dids": ["did:xybern:ws1:agent_compromised"]
    }
})

Agent-to-Agent Delegation (A2A Auth) NEW

When Agent A tells Agent B to perform an action, both sides must be authorised. Xybern intermediates every agent-to-agent interaction with bidirectional authorisation, scope attenuation, and cryptographic delegation grants recorded in the Provenance Vault.

Why this matters: Multi-agent frameworks (CrewAI, AutoGen, LangGraph) let agents freely call each other with zero authorisation. Xybern ensures Agent A is authorised to delegate, Agent B is authorised to accept, and the delegated scopes are the intersection of both — not a superset.

Core Concepts

ConceptDescription
delegation_policyPer-agent config: can_delegate, can_accept_delegation, delegable_scopes, acceptable_scopes, max_delegation_depth
Scope AttenuationGranted scopes = source's delegable ∩ target's acceptable ∩ requested. Wildcards supported (trade:*).
Delegation GrantFirst-class verifiable token (dlg_...) that the target agent presents when acting on behalf of the source.
Delegation ChainsAgent B can re-delegate to Agent C if depth allows. Max depth enforced, cascading revocation propagates down the chain.

Register Agents with Delegation Policy

import requests

BASE = "https://xybern.com/api/v1"
HEADERS = {"X-API-Key": "xb_...", "Content-Type": "application/json"}

# Source agent — can delegate trade:read and db:read
requests.post(f"{BASE}/enforce/agents", headers=HEADERS, json={
    "name": "finance-agent",
    "framework": "crewai",
    "permissions": {"allowed_action_types": ["execute_trade", "query_database"]},
    "scopes": ["trade:write", "trade:read", "db:read", "agent:delegate"],
    "delegation_policy": {
        "can_delegate": True,
        "can_accept_delegation": False,
        "delegable_scopes": ["trade:read", "db:read"],
        "max_delegation_depth": 3
    }
})

# Target agent — accepts trade:read and db:read
requests.post(f"{BASE}/enforce/agents", headers=HEADERS, json={
    "name": "analyst-agent",
    "framework": "crewai",
    "permissions": {"allowed_action_types": ["query_database", "read_data"]},
    "scopes": ["trade:read", "db:read"],
    "delegation_policy": {
        "can_delegate": False,
        "can_accept_delegation": True,
        "acceptable_scopes": ["trade:read", "db:read"]
    }
})

POST /v1/enforce/delegate

Request a delegation grant from one agent to another.

resp = requests.post(f"{BASE}/enforce/delegate", headers=HEADERS, json={
    "source_agent_id": "agent_abc123",
    "target_agent_id": "agent_def456",
    "scopes": ["trade:read", "db:read"],
    "action_types": ["query_database"],
    "constraints": {"max_amount": 100000},
    "instruction": "Analyse Q1 trading performance",
    "ttl_hours": 12,
    "max_uses": 5
})

grant = resp.json()["grant"]
# grant["grant_id"]          → "dlg_42eb33adab69"
# grant["attenuated_scopes"] → ["db:read", "trade:read"]
# grant["delegation_depth"]  → 1
# grant["vault_entry_id"]    → "ve_590dba34..."

POST /v1/enforce/delegate/verify

Verify a delegation grant before executing an action.

resp = requests.post(f"{BASE}/enforce/delegate/verify", headers=HEADERS, json={
    "grant_id": "dlg_42eb33adab69",
    "agent_id": "agent_def456",
    "action_type": "query_database"
})
# resp.json()["valid"]  → True
# resp.json()["reason"] → "Grant verified"

Using a Grant with Intercept

Pass grant_id to /enforce/intercept so the control plane verifies the delegation before policy evaluation.

resp = requests.post(f"{BASE}/enforce/intercept", headers=HEADERS, json={
    "action_type": "query_database",
    "action_content": "SELECT * FROM trades WHERE quarter='Q1'",
    "agent_id": "agent_def456",
    "grant_id": "dlg_42eb33adab69",
    "metadata": {"table": "trades", "operation": "read"}
})
# If grant is invalid or revoked → decision: "block"
# If grant is valid → normal policy evaluation proceeds

POST /v1/enforce/delegate/{grant_id}/revoke

Revoke a grant with cascading — all child grants in the delegation chain are revoked automatically.

resp = requests.post(f"{BASE}/enforce/delegate/dlg_42eb33adab69/revoke",
    headers=HEADERS, json={"reason": "task_complete"})
# resp.json()["revoked_count"]  → 1
# resp.json()["revoked_grants"] → ["dlg_42eb33adab69"]

GET /v1/enforce/delegations

List all delegation grants for the workspace. Filter by status (active, revoked, expired) or agent_id.

curl -H "X-API-Key: xb_..." \
  "https://xybern.com/api/v1/enforce/delegations?status=active"

Delegation Policies

Create enforcement policies of type delegation to control A2A behaviour at the workspace level.

requests.post(f"{BASE}/enforce/policies", headers=HEADERS, json={
    "name": "Block Finance→Marketing delegation",
    "policy_type": "delegation",
    "decision": "block",
    "conditions": {
        "blocked_pairs": [["agent_finance", "agent_marketing"]],
        "max_depth": 2,
        "blocked_scopes": ["payments:execute"]
    }
})

Policy Shadow Mode NEW

Test new policies against live production traffic without affecting any decisions. Shadow policies are evaluated on every incoming action but their verdicts are logged separately — the actual authorisation decision is never changed.

Use case: Before deploying a policy that blocks all trades over $100K, run it in shadow mode for 24 hours. The Policy Simulation dashboard shows exactly which actions would have been blocked, which agents would be affected, and whether any outcomes would change — all with zero production risk.

How It Works

StepWhat Happens
1. CreateCreate a policy with "mode": "shadow". It's active but observe-only.
2. EvaluateEvery /enforce/intercept call evaluates shadow policies in a separate pass after the real decision.
3. RecordShadow results are stored in shadow_results on the decision record: what the shadow policy would have decided, which policies triggered, whether the outcome would differ.
4. ReportThe /enforce/shadow/report endpoint and the Policy Simulation dashboard aggregate the impact.
5. PromoteWhen satisfied, promote to live with a single API call or one click in the dashboard.

Create a Shadow Policy

resp = requests.post(f"{BASE}/enforce/policies", headers=HEADERS, json={
    "name": "Block Trades Over 500K",
    "policy_type": "threshold",
    "action_types": ["execute_trade"],
    "decision": "block",
    "priority": 200,
    "trust_threshold": 95,
    "mode": "shadow"        # ← observe-only
})
# resp.json()["policy"]["mode"] → "shadow"

Shadow Results on Decisions

After sending traffic, each decision record includes a shadow_results field:

{
  "decision": "allow",
  "shadow_results": {
    "shadow_decision": "block",
    "would_have_changed": true,
    "live_decision": "allow",
    "triggered": [
      {
        "policy_id": "abc123",
        "name": "Block Trades Over 500K",
        "decision": "block",
        "reason": "Agent trust 50.2 < threshold 95"
      }
    ],
    "reasoning": "Agent trust 50.2 < threshold 95"
  }
}

GET /v1/enforce/shadow/report

Aggregate impact report for all active shadow policies over a time window.

curl -H "X-API-Key: xb_..." \
  "https://xybern.com/api/v1/enforce/shadow/report?hours=24"
{
  "shadow_policies": 2,
  "hours": 24,
  "report": {
    "total_evaluated": 847,
    "would_block": 23,
    "would_escalate": 8,
    "would_change_outcome": 15,
    "affected_agents": ["agent_abc", "agent_def"],
    "per_policy": [
      {
        "policy_id": "abc123",
        "name": "Block Trades Over 500K",
        "would_block": 23,
        "would_escalate": 0,
        "total_triggered": 23
      }
    ]
  }
}

POST /v1/enforce/shadow/{policy_id}/promote

Promote a shadow policy to live mode — it will start enforcing immediately.

resp = requests.post(
    f"{BASE}/enforce/shadow/{policy_id}/promote",
    headers=HEADERS
)
# resp.json()["policy"]["mode"] → "live"

Toggle Mode via Update

You can also switch any policy between live and shadow using the standard update endpoint:

requests.put(f"{BASE}/enforce/policies/{policy_id}",
    headers=HEADERS,
    json={"mode": "shadow"}  # or "live"
)

Dashboard

The Sentinel dashboard includes:

  • Policies tab — every policy shows a LIVE or SHADOW badge with a one-click mode toggle.
  • Policy Simulation tab — impact stats (would-block, would-escalate, outcome changes), per-policy breakdown, live vs shadow comparison table, and a "Promote to Live" button.

Temporal Permission Windows NEW

Grant time-bounded permissions to AI agents that auto-expire. Modeled after Just-In-Time (JIT) access patterns in human IAM — AWS STS temporary credentials, CyberArk JIT provisioning, HashiCorp Vault dynamic secrets — but purpose-built for AI agent authorization.

No standing access. An agent gets exactly the permissions it needs, for exactly the duration of its workflow, and those permissions auto-revoke when the window closes. Every lifecycle event (creation, use, extension, revocation) is anchored in the Provenance Vault.
ConceptDescription
TemporalPermissionWindowA time-bounded authorization grant with scopes, action types, constraints, and auto-expiry.
Duration1 minute to 24 hours. Configurable per window.
ScopesSame scope system as credentials — trade:write, db:read, etc.
Max UsesOptional limit on how many times the window can be used within its time boundary.
ExtensionsWindows can be extended up to max_extensions times (default: 3). Each extension is vault-recorded.
Workflow BindingOptional workflow_id ties the window to a specific workflow execution.
Lazy ExpiryNo background sweeper — expiry is checked at intercept time for zero overhead.

Create a Temporal Permission Window

import requests

BASE = "https://xybern.com/api/v1"
HEADERS = {"Authorization": "Bearer xb_your_key"}

resp = requests.post(f"{BASE}/enforce/temporal-windows", headers=HEADERS, json={
    "agent_id": "agent_finance_01",
    "scopes": ["payments.read", "payments.execute", "db:read"],
    "duration_minutes": 30,
    "action_types": ["execute_trade", "query_database"],
    "constraints": {"max_amount": 50000},
    "reason": "Processing customer order #4821",
    "workflow_id": "wf_order_4821",
    "max_uses": 10,
    "max_extensions": 2
})

window = resp.json()["window"]
# window["window_id"]         → "tw_a1b2c3d4e5f6"
# window["expires_at"]        → "2026-04-01T14:30:00Z"
# window["remaining_seconds"] → 1800
# window["is_active"]         → True

How It Works at Intercept Time

When an agent makes a request through POST /v1/enforce/intercept, the control plane automatically checks for active temporal windows. If the agent has an active window that covers the requested action, the response includes a temporal_window field:

{
  "decision": "allow",
  "decision_id": "enf_abc123",
  "temporal_window": {
    "window_id": "tw_a1b2c3d4e5f6",
    "remaining_seconds": 1247,
    "reason": "Temporal window 'tw_a1b2c3d4e5f6' grants access (expires in 1247s)"
  }
}

Extend a Window

If a workflow is still running and needs more time, extend the window (up to max_extensions):

resp = requests.post(
    f"{BASE}/enforce/temporal-windows/{window_id}/extend",
    headers=HEADERS,
    json={
        "additional_minutes": 15,
        "reason": "Workflow still processing batch"
    }
)
# resp.json()["window"]["extensions"]      → 1
# resp.json()["window"]["remaining_seconds"] → updated

Revoke a Window

Immediately terminate a window before it expires:

resp = requests.post(
    f"{BASE}/enforce/temporal-windows/{window_id}/revoke",
    headers=HEADERS,
    json={"reason": "Workflow completed early"}
)
# resp.json()["window"]["status"] → "revoked"

Pre-flight Check

Check if an agent has an active window without consuming a use:

resp = requests.post(f"{BASE}/enforce/temporal-windows/check", headers=HEADERS, json={
    "agent_id": "agent_finance_01",
    "action_type": "execute_trade",
    "metadata": {"amount": 25000}
})
# resp.json()["has_active_window"] → True
# resp.json()["remaining_seconds"] → 847

List & Stats

# List all windows (filter by agent, status, workflow)
requests.get(f"{BASE}/enforce/temporal-windows?active_only=true", headers=HEADERS)

# Agent-specific windows
requests.get(f"{BASE}/enforce/agents/{agent_id}/temporal-windows", headers=HEADERS)

# Aggregate stats
requests.get(f"{BASE}/enforce/temporal-windows/stats", headers=HEADERS)
# → { active_windows, expired_windows, revoked_windows, total_uses, avg_duration_minutes, ... }

Dashboard

The Sentinel dashboard includes a dedicated Temporal Windows tab showing:

  • Active windows with real-time countdown timers, scope badges, and one-click extend/revoke
  • Window history — expired and revoked windows with usage stats
  • Aggregate stats — active count, total uses, average duration, agents with active windows

Breakglass Protocol NEW

Emergency override mechanism for blocked enforcement decisions. When a critical action is blocked by policy but must proceed, operators trigger a breakglass override — a time-limited bypass with mandatory justification, immutable audit trail, and post-incident review requirements.

⚠️

Use sparingly. Breakglass overrides bypass normal authorization. Every event is recorded in the Provenance Vault and requires post-incident review. A per-agent cooldown (max 3 per 30 minutes) prevents abuse.

ConceptDescription
BreakglassEventA time-limited authorization override with justification, severity, and audit trail
JustificationMandatory written rationale (min 10 chars) explaining why the override is needed
Severitycritical, high, or medium — controls visibility and review priority
CooldownMax 3 breakglass events per agent per 30-minute window
Auto-expireOverride auto-expires after configured duration (default 15 min, max 2 hours)
Post-incident reviewEvents require review annotation after the fact for compliance

Trigger a Breakglass Override

import requests

BASE = "https://www.xybern.com/api/v1"
HEADERS = {"X-API-Key": "xb_your_key"}

override = requests.post(f"{BASE}/enforce/breakglass", headers=HEADERS, json={
    "agent_id": "agent_deploy_01",
    "action_type": "deploy:production",
    "justification": "Critical hotfix for payment processing outage — approved by VP Eng",
    "triggered_by": "oncall_engineer_42",
    "severity": "critical",
    "duration_minutes": 15,
    "max_actions": 5,
})

bg = override.json()["event"]
print(f"Override active: {bg['breakglass_id']}")
print(f"Expires in: {bg['remaining_seconds']}s")

How It Works at Intercept Time

When an action is blocked and the agent has an active breakglass override, the decision is automatically flipped to allow with a breakglass proof attached:

{
  "decision": "allow",
  "decision_id": "enf_abc123",
  "decision_path": "breakglass",
  "reasoning": "Policy violation detected | Breakglass override active (bg_xyz789)",
  "breakglass": {
    "breakglass_id": "bg_xyz789",
    "remaining_seconds": 842,
    "reason": "Breakglass override 'bg_xyz789' active (expires in 842s)"
  }
}

When a decision is blocked and no active override exists, the response includes a hint:

{
  "decision": "block",
  "breakglass_available": true
}

Close an Override Early

requests.post(f"{BASE}/enforce/breakglass/{bg_id}/close", headers=HEADERS, json={
    "reason": "Hotfix deployed successfully, no longer needed"
})

Add Post-Incident Review

requests.post(f"{BASE}/enforce/breakglass/{bg_id}/review", headers=HEADERS, json={
    "reviewed_by": "security_lead_01",
    "review_notes": "Override was justified — payment outage affected 12k users. "
                    "Root cause: stale policy blocking deploy:production for this agent. "
                    "Action: Updated policy to allow during incident windows."
})

List & Stats

# List all breakglass events
requests.get(f"{BASE}/enforce/breakglass", headers=HEADERS)

# Active overrides only
requests.get(f"{BASE}/enforce/breakglass?active_only=true", headers=HEADERS)

# Aggregate stats
requests.get(f"{BASE}/enforce/breakglass/stats", headers=HEADERS)
# → { total_events, active_overrides, pending_review, reviewed, by_severity, ... }

Dashboard

The Sentinel dashboard includes a dedicated Breakglass Protocol tab showing:

  • Active overrides with real-time countdown timers, severity badges, and one-click close
  • Event history — closed and expired events with review status indicators
  • Severity breakdown — critical/high/medium counts at a glance
  • Post-incident review — review button on unreviewed events, notes visible inline

Policy-as-Code SDK NEW

Define authorization policies as Python code, version them in Git, and deploy atomically via the SDK. Xybern diffs your policy definitions against the current state and creates, updates, or removes policies automatically — with full Provenance Vault tracking.

ConceptDescription
PolicyA fluent builder for a single enforcement policy — .on(), .when_*(), .block()
PolicyPackA versioned collection of policies deployed as one atomic unit
PolicyClientSDK sub-client at client.policies for deploy, rollback, validate operations
DeployCreate/update/delete policies from a pack — POST /v1/enforce/policy-packs
RollbackRevert to previous pack version — POST /v1/enforce/policy-packs/:name/rollback
ValidateDry-run a pack without deploying — POST /v1/enforce/policy-packs/validate
Source HashSHA-256 fingerprint of policy definitions — idempotent redeployments

Define Policies with the Python DSL

from xybern import Xybern, PolicyPack, Policy

client = Xybern(api_key="xb_your_key")

pack = PolicyPack("finance-controls", version="2.0.0",
                  description="Production trading controls")

# Block low-trust agents from executing trades
pack.add(Policy("Block Untrusted Traders")
    .on("execute_trade")
    .when_threshold(trust_below=50)
    .block("Agents with trust < 50 cannot trade"))

# No deployments on weekends
pack.add(Policy("Weekend Deploy Freeze")
    .on("deploy:*")
    .when_time(blocked_days=[6, 7])
    .escalate("Weekend deploys require human approval"))

# Detect PII in prompts
pack.add(Policy("PII Scanner")
    .on("send_prompt")
    .when_content_matches(r"\b\d{3}-\d{2}-\d{4}\b", r"\bSSN\b")
    .block("PII detected in prompt content"))

# Limit delegation chain depth
pack.add(Policy("Chain Depth Guard")
    .when_chain(max_depth=3)
    .escalate("Delegation chain too deep"))

Deploy a Policy Pack

# Deploy to live enforcement
result = client.policies.deploy(pack)
print(result)
# → {"ok": true, "pack": {...}, "summary": {"created": 4, "updated": 0, "deleted": 0}}

# Deploy in shadow mode (observe only, never enforced)
result = client.policies.deploy(pack, deploy_mode="shadow")

# Redeploy with changes — Xybern auto-diffs
pack_v2 = PolicyPack("finance-controls", version="2.1.0")
pack_v2.add(Policy("Block Untrusted Traders")
    .on("execute_trade")
    .when_threshold(trust_below=60)  # raised threshold
    .block("Agents with trust < 60 cannot trade"))

result = client.policies.deploy(pack_v2)
# → {"summary": {"created": 0, "updated": 1, "deleted": 3, "unchanged": 0}}

Validate Without Deploying

# Dry-run validation
validation = client.policies.validate(pack)
print(validation)
# → {"ok": true, "valid": true, "policy_count": 4, "policies": [...]}

Rollback to Previous Version

# Something went wrong — rollback instantly
result = client.policies.rollback("finance-controls")
# → redeploys previous version's policies

Delete a Pack

# Remove a pack and all its managed policies
result = client.policies.delete("finance-controls")
# → {"ok": true, "policies_removed": 4}

REST API Reference

  • POST /v1/enforce/policy-packs — Deploy a policy pack
  • GET /v1/enforce/policy-packs — List all packs
  • GET /v1/enforce/policy-packs/:name — Get a specific pack
  • POST /v1/enforce/policy-packs/:name/rollback — Rollback to previous version
  • DELETE /v1/enforce/policy-packs/:name — Delete a pack
  • POST /v1/enforce/policy-packs/validate — Dry-run validation
  • GET /v1/enforce/policy-packs/stats — Pack statistics

Dashboard

The Sentinel Control Plane includes a dedicated Policy-as-Code view showing all active packs, their version, policy count, deploy mode, source hash, and deployment timestamp. From the dashboard you can rollback or delete packs with one click.

Federation NEW

Enable secure, policy-controlled interactions between AI agents across different Xybern workspaces and organizations. Federation links establish bilateral trust, and short-lived tokens allow scoped cross-org actions.

ConceptDescription
Federation LinkA trust relationship between two workspaces (source → target) with configurable guardrails
Federation TokenA short-lived, scope-limited, use-counted token for cross-org agent calls
Trust CapMaximum trust level for external agents (prevents foreign agents from exceeding local thresholds)
Directionoutbound (you → partner) or inbound (partner → you)

Propose a Federation Link

from xybern import Xybern

client = Xybern(api_key="xb_your_key")

# Propose federation to a partner organization
link = client.federation.propose(
    target_workspace_id="partner_workspace_id",
    source_org_name="Acme Corp",
    target_org_name="Partner Inc",
    allowed_action_types=["data:read", "data:query"],
    allowed_scopes=["read:market_data"],
    max_trust_level=40.0,        # External agents capped at 40
    expires_in_days=90,           # Link expires after 90 days
)
print(link["link_id"])           # fed_a1b2c3d4e5f6
print(link["shared_secret"])     # Exchange this securely

Accept an Inbound Link (Partner Side)

# Partner accepts the pending link
partner_client = Xybern(api_key="xb_partner_key")
result = partner_client.federation.accept(
    link_id="fed_a1b2c3d4e5f6",
    approved_by="security-team",
)
# Both sides now show status: "active"

Issue a Cross-Org Token

# Source org issues a token for one of its agents
token = client.federation.issue_token(
    link_id="fed_a1b2c3d4e5f6",
    agent_id="research-agent-01",
    scopes=["read:market_data"],
    action_types=["data:read"],
    ttl_seconds=300,     # 5-minute lifetime
    max_uses=10,         # Up to 10 uses
)
print(token["token"])    # Short-lived credential
print(token["expires_in"])  # 300

Use Token in Cross-Org Intercept

# Agent includes the federation token in its intercept call
result = partner_client.agents.intercept(
    action_type="data:read",
    action_content="Query market data for AAPL",
    credential="./my_agent.cred",
    federation_token=token["token"],
)
# Response includes federation proof:
# result.raw["federation"]["source_org"]  → "Acme Corp"
# result.raw["federation"]["trust_cap"]   → 40.0

REST API Reference

MethodEndpointDescription
POST/v1/enforce/federation/linksPropose a new federation link
GET/v1/enforce/federation/linksList all federation links
POST/v1/enforce/federation/links/:id/acceptAccept a pending link
POST/v1/enforce/federation/links/:id/suspendSuspend an active link
POST/v1/enforce/federation/links/:id/revokePermanently revoke a link
POST/v1/enforce/federation/tokensIssue a cross-org token
GET/v1/enforce/federation/statsGet federation statistics

Sentinel Dashboard

The Sentinel Control Plane includes a dedicated Federation view showing all active links, pending invites, token counts, and federated action totals. You can accept, suspend, or revoke links directly from the dashboard.

Human-in-the-Loop Escalation Gating NEW

When a policy decision is escalate, the agent can now block and wait until a human reviews the action on the Sentinel Control Plane dashboard. This is controlled by the wait_on_escalate parameter on the @guard decorator (default: True).

Full Flow

  1. Agent calls the guarded function → Sentinel intercepts and returns decision: "escalate" with an escalation_id.
  2. SDK polls GET /v1/enforce/escalations/{id}/status every escalation_poll_interval seconds.
  3. Human opens Sentinel → Control Plane, sees the pending escalation card with Approve and Reject buttons.
  4. If approved: agent unblocks and the wrapped function executes normally.
  5. If rejected (or timeout exceeded): AgentBlockedError is raised and the function never runs.

Code Example

from xybern import Xybern, AgentCredential, AgentBlockedError

client = Xybern(api_key="xb_your_key")
cred   = AgentCredential.load("./bot.cred")

@client.agents.guard(
    credential=cred,
    wait_on_escalate=True,      # Block until human decides (default)
    escalation_timeout=300,     # Wait up to 5 minutes
    escalation_poll_interval=5, # Check every 5 seconds
)
def wire_transfer(amount_usd: float, recipient: str):
    # Only executes if a human approves on the Sentinel dashboard
    execute_wire(amount_usd, recipient)

try:
    wire_transfer(50000, "vendor@example.com")
except AgentBlockedError as e:
    print(f"Transfer rejected or timed out: {e}")

InterceptResult Attributes

AttributeTypeDescription
.decisionstr"allow", "block", or "escalate"
.trust_scorefloatAgent trust score at time of decision (0-100)
.decision_idstrImmutable decision record ID
.escalation_idstr | NoneSet when decision == "escalate"; used to poll for human resolution
.policy_namestr | NoneName of the policy that triggered this decision
.latency_msintDecision latency in milliseconds

wait_for_escalation() Method

The @guard decorator calls this automatically when wait_on_escalate=True. You can also call it directly when handling escalations manually.

# Manual escalation polling (when wait_on_escalate=False)
result = client.agents.intercept(
    action_type="wire_transfer",
    action_content="Transfer $50,000 to vendor",
    agent_id=cred.agent_id,
)

if result.decision == "escalate":
    resolution = client.agents.wait_for_escalation(
        escalation_id=result.escalation_id,
        timeout=300,        # seconds
        poll_interval=5,    # seconds
    )
    # resolution == "approved" or "rejected"
    if resolution == "approved":
        execute_wire(50000, "vendor@example.com")

Escalation Status Endpoint

GET /v1/enforce/escalations/{escalation_id}/status

Poll the resolution status of a pending escalation. Returns {"ok": true, "status": "pending"|"approved"|"rejected"}. Used internally by the SDK's wait_for_escalation() method.

Dashboard: Approve / Reject

Pending escalations appear as cards in Sentinel → Control Plane. Each card shows the action details, agent identity, and two buttons: Approve and Reject. Resolving a card immediately unblocks any waiting SDK call.

The underlying endpoint is: POST /api/sentinel/enforcement/escalations/{escalation_id}/resolve with body {"resolution": "approved"|"rejected"}.

💡

Set wait_on_escalate=False if you want fire-and-forget escalation behaviour — the SDK returns immediately with decision="escalate" and you handle the escalation_id asynchronously.

More Authorisation Examples

Common patterns for working with the Authorisation API beyond basic intercept calls.

Registering an Agent Identity

import requests

response = requests.post(
    "https://www.xybern.com/api/v1/enforce/agents",
    headers={"X-API-Key": "xb_your_api_key"},
    json={
        "agent_id": "finance_agent_001",
        "name": "Finance Reporting Agent",
        "role": "financial_analyst",
        "allowed_tools": ["read_database", "generate_report", "send_email"],
        "denied_tools": ["delete_records", "modify_schema"],
        "max_delegation_depth": 2
    }
)
print(response.json()["status"])  # "registered"

Creating a Policy

response = requests.post(
    "https://www.xybern.com/api/v1/enforce/policies",
    headers={"X-API-Key": "xb_your_api_key"},
    json={
        "policy_id": "no_external_email_without_approval",
        "name": "Block external email without human approval",
        "rules": [
            {
                "condition": {
                    "action.tool": "send_email",
                    "action.parameters.to": {"not_contains": "@company.com"}
                },
                "effect": "DENY",
                "reason": "External emails require human-in-the-loop approval"
            }
        ],
        "shadow_mode": False
    }
)

A2A Delegation

response = requests.post(
    "https://www.xybern.com/api/v1/enforce/delegate",
    headers={"X-API-Key": "xb_your_api_key"},
    json={
        "delegator_agent_id": "orchestrator_agent",
        "delegate_agent_id": "sub_agent_001",
        "scoped_tools": ["read_database", "generate_report"],
        "expires_in_seconds": 1800,
        "context": {"task": "q4_report", "initiated_by": "orchestrator_agent"}
    }
)
print(response.json()["delegation_token"])

Breakglass — Emergency Override

response = requests.post(
    "https://www.xybern.com/api/v1/enforce/breakglass",
    headers={"X-API-Key": "xb_your_api_key"},
    json={
        "agent_id": "incident_response_agent",
        "reason": "Production incident — database migration requires elevated access",
        "requested_tools": ["modify_schema", "delete_records"],
        "duration_seconds": 900,
        "approver_id": "admin_user_007"
    }
)
# Breakglass events are always logged regardless of outcome
print(response.json()["breakglass_token"])

Framework Integrations

Drop Xybern authorisation into your existing agent stack. Each integration wraps your tools or callbacks so every action is intercepted before execution — no changes to your agent logic required.

CrewAI

Wrap any CrewAI tool with a Xybern authorisation check. The guard function calls /enforce/intercept before the tool executes and raises PermissionError on a DENY decision.

# crewai_xybern.py
import requests
from crewai import Agent, Task, Crew, Tool

XYBERN_API_KEY = "xb_your_api_key"

def xybern_intercept(agent_id: str, tool_name: str, tool_input: dict) -> dict:
    """Call Xybern before every tool execution."""
    return requests.post(
        "https://www.xybern.com/api/v1/enforce/intercept",
        headers={"X-API-Key": XYBERN_API_KEY},
        json={
            "agent_id": agent_id,
            "action": {"type": "tool_call", "tool": tool_name, "parameters": tool_input},
            "context": {"framework": "crewai"}
        }
    ).json()

def guarded_tool(agent_id: str, tool_fn, tool_name: str):
    """Wrap any CrewAI tool with Xybern authorisation."""
    def wrapper(input_data: dict):
        check = xybern_intercept(agent_id, tool_name, input_data)
        if check["decision"] == "DENY":
            raise PermissionError(f"Xybern denied: {check['reason']}")
        return tool_fn(input_data)
    return wrapper

def raw_send_email(data: dict):
    print(f"Sending email to {data['to']}")

guarded_email = guarded_tool("finance_agent_001", raw_send_email, "send_email")

finance_agent = Agent(
    role="Financial Analyst",
    goal="Generate quarterly reports",
    backstory="Expert in financial analysis",
    tools=[Tool(name="send_email", func=guarded_email, description="Send email with Xybern authorisation")]
)

task = Task(description="Generate and send the Q4 report", agent=finance_agent)
crew = Crew(agents=[finance_agent], tasks=[task])
result = crew.kickoff()

AutoGen

Wrap AutoGen's function_map so every function call is authorised before execution. DENY decisions return a safe error string rather than raising, keeping the conversation intact.

# autogen_xybern.py
import requests
import autogen

XYBERN_API_KEY = "xb_your_api_key"

def authorise_action(agent_name: str, tool: str, params: dict) -> bool:
    resp = requests.post(
        "https://www.xybern.com/api/v1/enforce/intercept",
        headers={"X-API-Key": XYBERN_API_KEY},
        json={
            "agent_id": agent_name,
            "action": {"type": "tool_call", "tool": tool, "parameters": params},
            "context": {"framework": "autogen"}
        }
    ).json()
    return resp["decision"] == "ALLOW"

config_list = [{"model": "gpt-4", "api_key": "your_openai_key"}]

def guarded_function_map(agent_name: str, function_map: dict) -> dict:
    """Wrap an AutoGen function_map with Xybern checks."""
    guarded = {}
    for fn_name, fn in function_map.items():
        def make_guarded(name, original_fn):
            def guarded_fn(**kwargs):
                if not authorise_action(agent_name, name, kwargs):
                    return f"Action '{name}' was denied by Xybern authorisation."
                return original_fn(**kwargs)
            return guarded_fn
        guarded[fn_name] = make_guarded(fn_name, fn)
    return guarded

def query_database(table: str, query: str):
    return f"Results from {table}: [...]"

analyst = autogen.AssistantAgent(
    name="analyst_agent",
    llm_config={"config_list": config_list}
)

user_proxy = autogen.UserProxyAgent(
    name="user_proxy",
    human_input_mode="NEVER",
    function_map=guarded_function_map("analyst_agent", {"query_database": query_database})
)

user_proxy.initiate_chat(analyst, message="Query the customers table and summarise the data.")

LangGraph

Add a Xybern gate as a dedicated node in your LangGraph state graph. The gate runs before any execution node and sets an xybern_approved flag in state — the execution node checks this flag before proceeding.

# langgraph_xybern.py
import requests
from typing import TypedDict
from langgraph.graph import StateGraph, END
from langchain_core.messages import HumanMessage, AIMessage

XYBERN_API_KEY = "xb_your_api_key"

class AgentState(TypedDict):
    messages: list
    next_action: str
    xybern_approved: bool

def xybern_gate(state: AgentState) -> AgentState:
    """LangGraph node: authorise the next action before execution."""
    next_action = state.get("next_action", "")
    if not next_action:
        return {**state, "xybern_approved": True}

    resp = requests.post(
        "https://www.xybern.com/api/v1/enforce/intercept",
        headers={"X-API-Key": XYBERN_API_KEY},
        json={
            "agent_id": "langgraph_agent_001",
            "action": {"type": "tool_call", "tool": next_action, "parameters": {}},
            "context": {"framework": "langgraph", "state_step": len(state["messages"])}
        }
    ).json()

    return {**state, "xybern_approved": resp["decision"] == "ALLOW"}

def execute_action(state: AgentState) -> AgentState:
    if not state["xybern_approved"]:
        msg = AIMessage(content="Action denied by Xybern authorisation policy.")
        return {**state, "messages": state["messages"] + [msg]}
    msg = AIMessage(content=f"Executing {state['next_action']}...")
    return {**state, "messages": state["messages"] + [msg], "next_action": ""}

graph = StateGraph(AgentState)
graph.add_node("xybern_gate", xybern_gate)
graph.add_node("execute", execute_action)
graph.set_entry_point("xybern_gate")
graph.add_edge("xybern_gate", "execute")
graph.add_edge("execute", END)

app = graph.compile()
result = app.invoke({
    "messages": [HumanMessage(content="Run report")],
    "next_action": "generate_report",
    "xybern_approved": False
})

LlamaIndex

Use make_guarded_tool to wrap any LlamaIndex FunctionTool with a Xybern check. Denied actions return a safe string that the agent can interpret without crashing.

# llamaindex_xybern.py
import requests
from llama_index.core.tools import FunctionTool
from llama_index.core.agent import ReActAgent
from llama_index.llms.openai import OpenAI

XYBERN_API_KEY = "xb_your_api_key"
AGENT_ID = "llamaindex_agent_001"

def xybern_check(tool_name: str, params: dict) -> bool:
    resp = requests.post(
        "https://www.xybern.com/api/v1/enforce/intercept",
        headers={"X-API-Key": XYBERN_API_KEY},
        json={
            "agent_id": AGENT_ID,
            "action": {"type": "tool_call", "tool": tool_name, "parameters": params},
            "context": {"framework": "llamaindex"}
        }
    ).json()
    return resp["decision"] == "ALLOW"

def make_guarded_tool(name: str, description: str, fn):
    """Wrap a LlamaIndex tool function with Xybern authorisation."""
    def guarded(**kwargs):
        if not xybern_check(name, kwargs):
            return f"Xybern denied action: {name}. Check your authorisation policies."
        return fn(**kwargs)
    return FunctionTool.from_defaults(fn=guarded, name=name, description=description)

def search_documents(query: str) -> str:
    return f"Documents matching '{query}': [doc1, doc2]"

def write_report(content: str, filename: str) -> str:
    return f"Report written to {filename}"

tools = [
    make_guarded_tool("search_documents", "Search internal documents", search_documents),
    make_guarded_tool("write_report", "Write a report to disk", write_report),
]

llm = OpenAI(model="gpt-4")
agent = ReActAgent.from_tools(tools, llm=llm, verbose=True)
response = agent.chat("Find all documents about Q4 and write a summary report.")
print(response)

Custom Pipelines

Use the XybernClient class and its @guard decorator to wrap any Python function in your own agent pipeline. Works with any orchestration system — no framework dependency required.

# custom_pipeline_xybern.py
import requests
from functools import wraps

XYBERN_API_KEY = "xb_your_api_key"

class XybernClient:
    """Minimal Xybern client for custom agent pipelines."""

    def __init__(self, api_key: str, agent_id: str):
        self.api_key = api_key
        self.agent_id = agent_id
        self.base_url = "https://www.xybern.com/api/v1"

    def _headers(self):
        return {"X-API-Key": self.api_key, "Content-Type": "application/json"}

    def intercept(self, tool: str, params: dict, context: dict = None) -> dict:
        payload = {
            "agent_id": self.agent_id,
            "action": {"type": "tool_call", "tool": tool, "parameters": params},
            "context": context or {}
        }
        return requests.post(
            f"{self.base_url}/enforce/intercept",
            headers=self._headers(),
            json=payload
        ).json()

    def guard(self, tool_name: str):
        """Decorator: wrap any function with Xybern authorisation."""
        def decorator(fn):
            @wraps(fn)
            def wrapper(*args, **kwargs):
                result = self.intercept(tool_name, kwargs)
                if result["decision"] == "DENY":
                    raise PermissionError(f"[Xybern] Denied: {result['reason']}")
                return fn(*args, **kwargs)
            return wrapper
        return decorator

# Usage
xybern = XybernClient(api_key=XYBERN_API_KEY, agent_id="custom_pipeline_agent")

@xybern.guard("send_payment")
def send_payment(amount: float, recipient: str, currency: str = "USD"):
    print(f"Sending {currency} {amount} to {recipient}")
    return {"status": "sent"}

@xybern.guard("read_customer_data")
def read_customer_data(customer_id: str, fields: list):
    return {"id": customer_id, "data": "..."}

# These will be authorised by Xybern before executing
send_payment(amount=50000.00, recipient="vendor@external.com", currency="USD")
read_customer_data(customer_id="cust_001", fields=["name", "email", "balance"])

Error Handling

Status Meaning
200 Success
400 Bad request
401 Invalid API key
429 Rate limited
500 Server error

Caching & Deduplication

Identical content verified within a 5-minute window returns the cached result instantly — no LLM call, no latency, no cost.

Cached responses include "cached": true in the response body so you can distinguish them from fresh verifications.

How It Works

  1. Content is hashed with SHA-256 on arrival
  2. The hash is checked against a per-workspace Redis cache
  3. If a match is found within the 5-minute TTL, the cached result is returned with a fresh verification_id
  4. If no match, the full verification pipeline runs and the result is cached

Agent RBAC (Role-Based Access Control)

Define reusable roles with granular permissions and assign them to agents. Roles are evaluated during every intercept call — if any active role denies the action type, the request is blocked before policies run.

ConceptDescription
RoleA named permission bundle with allowed/denied action types, scopes, and a minimum trust threshold.
AssignmentA many-to-many link between roles and agents. One agent can hold multiple roles.
InheritanceA role can inherit from a parent role, composing permissions up the hierarchy.
WildcardsAction type patterns like payment:* match any action starting with payment:.

Create a Role

from xybern import Xybern

client = Xybern(api_key="xb_your_key")

role = client.roles.create(
    name="finance-agent",
    description="Can read and transfer payments, but not admin actions",
    allowed_action_types=["payment:*", "report:read"],
    denied_action_types=["admin:*"],
    min_trust_level=60.0,
)
print(role["role"]["role_id"])

Assign a Role to an Agent

client.roles.assign(
    role_id="role_abc123",
    agent_id="agent_xyz789",
)

Role Inheritance

# Create a base "reader" role
reader = client.roles.create(
    name="reader",
    allowed_action_types=["*.read"],
)

# Create "analyst" that inherits from "reader" and adds more
analyst = client.roles.create(
    name="analyst",
    allowed_action_types=["report:generate", "data:query"],
    inherits_from=reader["role"]["role_id"],
)

How Enforcement Works

  1. Agent submits an action via client.agents.intercept(...)
  2. Control plane resolves the agent and loads all active roles
  3. If any role's denied list matches the action type → block
  4. If no role's allowed list matches → block
  5. If the agent's trust level is below the role's min_trust_levelblock
  6. Otherwise, proceed to policy evaluation

REST API Reference

MethodEndpointDescription
POST/v1/enforce/rolesCreate a role
GET/v1/enforce/rolesList roles
GET/v1/enforce/roles/:idGet a role
PUT/v1/enforce/roles/:idUpdate a role
DELETE/v1/enforce/roles/:idDelete a role
POST/v1/enforce/roles/:id/assignAssign role to agent
POST/v1/enforce/roles/:id/unassignUnassign role from agent
GET/v1/enforce/roles/:id/agentsList agents in role
GET/v1/enforce/roles/statsRBAC statistics

Dashboard

The Roles (RBAC) tab in the Sentinel dashboard displays all roles, their agent counts, allowed/denied action types, trust thresholds, and inheritance chains. You can delete roles directly from the UI.

Webhooks & Real-Time Event Streaming

Subscribe to authorization events and have them delivered to any HTTP endpoint in real time. Every payload is HMAC-SHA256 signed with your webhook secret.

Supported Event Types

EventFires when...
decision.blockAny agent action is blocked
decision.escalateA decision requires human review
decision.allowAn action is approved (opt-in, high volume)
breakglass.triggeredEmergency override activated
breakglass.deactivatedEmergency override ended
federation.link_proposedNew cross-org trust link proposed
federation.token_issuedCross-org token minted
policy.deployedA policy pack goes live
policy.rollbackA policy pack is rolled back
agent.registeredNew agent registered
agent.trust_changedAgent trust score crosses a threshold
rbac.role_assignedA role is assigned to an agent
temporal.window_openedA JIT access window opens
temporal.window_expiredA JIT access window closes

Create a Webhook

from xybern import Xybern

client = Xybern(api_key="xb_your_key")

hook = client.webhooks.create(
    url="https://hooks.slack.com/services/T0/B0/xxx",
    events=["decision.block", "breakglass.triggered"],
    description="Slack alerts for security events",
)
# Save hook["webhook"]["secret"] to verify signatures later
print(hook["webhook"]["webhook_id"])

Payload Format

{
  "event": "decision.block",
  "timestamp": "2026-04-01T19:32:14.003Z",
  "workspace_id": "f5300764...",
  "data": {
    "decision_id": "enf_a1b2c3d4e5f6",
    "agent_id": "agent_35c86445e7e2",
    "action_type": "admin:delete_workspace",
    "decision": "block",
    "decision_path": "rbac",
    "reasoning": "Action not allowed by agent roles",
    "trust_score": 22.8
  }
}

Signature Verification

Every delivery includes an X-Xybern-Signature header. Verify it server-side:

import hmac, hashlib

def verify_webhook(payload_bytes, signature_header, secret):
    expected = hmac.new(
        secret.encode(), payload_bytes, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature_header)

Retry Policy

Failed deliveries are retried with exponential backoff (1s, 2s, 4s, 8s, 16s). After 50 consecutive failures, the webhook is auto-disabled. Re-enable it via the API or dashboard.

REST API Reference

MethodEndpointDescription
POST/v1/enforce/webhooksCreate a subscription
GET/v1/enforce/webhooksList subscriptions
GET/v1/enforce/webhooks/:idGet a subscription
PUT/v1/enforce/webhooks/:idUpdate a subscription
DELETE/v1/enforce/webhooks/:idDelete a subscription
POST/v1/enforce/webhooks/:id/testSend a test event
GET/v1/enforce/webhooks/:id/deliveriesList delivery history
POST/v1/enforce/webhooks/:id/rotate-secretRotate signing secret
GET/v1/enforce/webhooks/statsWebhook statistics

Xybern API Documentation v1.21 · info@xybern.com