Agent Setup Guide
Connect any AI agent or autonomous script to SilentAuth so sensitive actions require human approval before executing. No SDK required — just HTTP.
On this page
Prerequisites
Before you start, you need:
- A SilentAuth account (free)
- A project created in the Projects dashboard
- Your project's Secret Key (from Agent Setup → Your API Credentials)
- Any HTTP client (requests, curl, fetch, httpx, etc.)
Core Concepts
A record of what your agent wants to do. Contains the action name, source, risk tier, and parameters. Created before execution.
A rule that matches intents by action pattern, risk tier, or source type and auto-resolves them — either auto-approve or auto-deny.
An intent status meaning no policy matched and a human must review it. Appears in the Execution Gate dashboard.
A short-lived signed JWT issued when an intent is approved. Your agent polls for it, validates it, then proceeds.
Register an Agent
Before your agent starts sending intents, register it in the AI Agent Authorization dashboard. Registration links a human-readable name to a unique fingerprint, enables status enforcement, and starts tracking when the agent was last seen.
| Field | Description |
|---|---|
| Name | Human-readable label for this agent (e.g. Refund Agent v2) |
| Fingerprint | Unique string sent as the source field in every intent. Auto-generated or custom. |
| Framework | Python, LangChain, OpenAI Assistants, AutoGen, CrewAI, or Other. Informational only. |
| Risk Sensitivity | Default risk tier for the integration snippet. Can be overridden per request. |
| Project | Optional. Scopes this agent to a specific project's API key. |
| Status | active | paused | revoked. Controls whether intents from this agent are accepted. |
source field on every intent.Agent Status Enforcement
Once registered, the agent's status is enforced by the API on every intent submission. This gives you a kill switch that takes effect instantly — no code changes or redeployments required.
activeNormal operation. Intents are accepted, validated against policies, and processed.
pausedIntents return HTTP 403 with error code AGENT_PAUSED. Use for maintenance or investigation.
revokedIntents return HTTP 403 with error code AGENT_REVOKED. Use for decommissioned or compromised agents.
{
"data": null,
"error": {
"code": "AGENT_PAUSED",
"message": "Agent \"Refund Agent v2\" is paused. Resume the agent in the dashboard before submitting intents."
}
}When an intent is successfully submitted from a registered agent, last_seen_at is updated automatically — giving you a live activity signal per agent without any extra tracking code.
Step 1 — Create an Intent
Before executing any sensitive action, your agent sends a POST /api/intents request. This is the single integration point — everything else happens asynchronously.
curl -X POST https://silentauth.ai/api/intents \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"action": "issue_refund",
"source_type": "ai_agent",
"source": "refund-agent-v1",
"risk_tier": "high",
"parameters": {
"customer_id": "cust_4821",
"amount": 50.00
}
}'Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| action | string | Yes | Name of the action your agent wants to perform |
| source_type | string | Yes | ai_agent | devops | automation |
| source | string | No | Agent fingerprint. If it matches a registered agent, status is enforced and last_seen_at is updated. |
| risk_tier | string | No | low | medium | high | critical (default: medium) |
| parameters | object | No | Arbitrary key-value pairs — shown in the dashboard during review |
| project_id | uuid | No | Override project association (auto-detected from API key) |
Step 2 — Handle the Decision
The API responds immediately with a decision field. This tells your agent what happened right now — before any human has reviewed it.
{
"id": "a3f2c1b0-4e52-4d1a-9b0f-12c34d56e789",
"decision": "require_approval",
"data": {
"id": "a3f2c1b0-...",
"status": "pending",
"action": "issue_refund",
"risk_tier": "high",
"source": "refund-agent-v1",
"parameters": { "customer_id": "cust_4821", "amount": 50.00 },
"created_at": "2026-03-16T12:00:00.000Z"
}
}Decision values
auto_approve— Auto-approved by policyA policy matched and auto-approved. The permit_token is immediately available in data.permit_token. Proceed.
auto_deny— Auto-denied by policyA policy matched and blocked this action. data.status is 'denied'. Abort and surface the error to your caller.
require_approval— Awaiting human reviewNo policy matched. data.status is 'pending'. Your agent must now poll or use webhooks to wait for a human decision.
Step 3 — Poll for Approval
When decision is require_approval, your agent blocks and polls until a human resolves the intent in the dashboard.
import requests, time
def wait_for_approval(intent_id, secret_key, timeout_seconds=600):
"""Block until the intent is approved or denied."""
headers = {"Authorization": f"Bearer {secret_key}"}
base = "https://silentauth.ai"
deadline = time.time() + timeout_seconds
while time.time() < deadline:
time.sleep(5)
# Check approved intents
approved = requests.get(
f"{base}/api/intents?status=approved",
headers=headers,
).json()
for intent in approved.get("data", []):
if intent["id"] == intent_id:
return intent["permit_token"] # ✓ approved
# Check denied intents
denied = requests.get(
f"{base}/api/intents?status=denied",
headers=headers,
).json()
for intent in denied.get("data", []):
if intent["id"] == intent_id:
raise PermissionError(f"Intent denied: {intent_id}")
raise TimeoutError(f"Timed out after {timeout_seconds}s")Step 4 — Use the Permit Token
Once you have a permit_token, pass it through to your backend action. The permit is a signed JWT — your backend can verify it without a network call. Permits expire after 5 minutes.
def issue_refund(customer_id: str, amount: float):
# Step 1: Declare intent
resp = requests.post(
"https://silentauth.ai/api/intents",
headers={"Authorization": f"Bearer {SECRET_KEY}"},
json={
"action": "issue_refund",
"source_type": "ai_agent",
"risk_tier": "high",
"parameters": {"customer_id": customer_id, "amount": amount},
},
)
result = resp.json()
if result["decision"] == "auto_deny":
raise PermissionError("Blocked by policy")
if result["decision"] == "auto_approve":
permit = result["data"]["permit_token"]
else:
# Blocks here until a human approves (or timeout)
permit = wait_for_approval(result["id"], SECRET_KEY)
# Step 4: Execute — pass permit to your backend
payment_service.refund(
customer_id=customer_id,
amount=amount,
permit_token=permit, # Backend verifies this
)EXECUTION_SECRET. It contains: intent_id, action, risk_tier, iat, and exp.Using Webhooks Instead of Polling
Configure a webhook in Gate Webhooks and SilentAuth will POST to your endpoint the moment an intent is approved or denied. No polling loop required.
{
"event": "intent.approved",
"intent_id": "a3f2c1b0-...",
"project_id": "proj_...",
"action": "issue_refund",
"source": "refund-agent-v1",
"source_type": "ai_agent",
"risk_tier": "high",
"status": "approved",
"parameters": { "customer_id": "cust_4821", "amount": 50.00 },
"permit_token": "eyJhbGciOiJIUzI1NiJ9...",
"approved_by": "admin@example.com",
"timestamp": "2026-03-16T12:34:56.000Z"
}Verifying webhook signatures
Every webhook request includes an X-SilentAuth-Signature header. Always verify it to reject forged requests.
import hmac, hashlib
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "whsec_your_signing_secret"
@app.route("/webhooks/silentauth", methods=["POST"])
def handle_webhook():
body = request.get_data()
sig = request.headers.get("X-SilentAuth-Signature", "")
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(),
body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, sig):
abort(401) # Invalid signature — reject
payload = request.json
if payload["event"] == "intent.approved":
permit_token = payload["permit_token"]
intent_id = payload["intent_id"]
# Resume the waiting agent thread here
resolve_pending_intent(intent_id, permit_token)
return "", 200Automating with Policies
Policies let you define rules that resolve intents instantly without human review. Set them up in Execution Policies.
| Policy field | Values | Description |
|---|---|---|
| action_pattern | glob string | Match action names. Supports * wildcard. E.g. read_* matches all reads. |
| source_type | any | ai_agent | devops | automation | Match by intent origin type. |
| risk_tier | any | low | medium | high | critical | Match by risk tier. |
| decision | auto_approve | auto_deny | require_approval | What to do when this policy matches. |
| priority | integer (higher = evaluated first) | Policies are checked in descending priority. First match wins. |
auto_approve policy for risk_tier=low, and leave everything else to fall through to require_approval. You get speed for safe actions and control for risky ones.Full Python Example
A self-contained integration you can copy and adapt. No external dependencies beyond requests.
import requests, time, os
SECRET_KEY = os.environ["SILENTAUTH_SECRET_KEY"]
BASE_URL = "https://silentauth.ai"
def require_approval(action, risk_tier="medium", source="my-agent", parameters=None):
"""Declare an intent and block until a human approves or policy resolves it."""
headers = {
"Authorization": f"Bearer {SECRET_KEY}",
"Content-Type": "application/json",
}
resp = requests.post(
f"{BASE_URL}/api/intents",
headers=headers,
json={
"action": action,
"source_type": "ai_agent",
"source": source,
"risk_tier": risk_tier,
"parameters": parameters or {},
},
)
resp.raise_for_status()
result = resp.json()
intent_id = result["id"]
decision = result.get("decision", "require_approval")
if decision == "auto_approve":
return result["data"]["permit_token"]
if decision == "auto_deny":
raise PermissionError(f"Intent {intent_id} was auto-denied by policy")
# Poll until human resolves (timeout: 10 minutes)
deadline = time.time() + 600
while time.time() < deadline:
time.sleep(5)
approved = requests.get(
f"{BASE_URL}/api/intents?status=approved",
headers=headers,
).json()
for intent in approved.get("data", []):
if intent["id"] == intent_id:
return intent["permit_token"]
denied = requests.get(
f"{BASE_URL}/api/intents?status=denied",
headers=headers,
).json()
for intent in denied.get("data", []):
if intent["id"] == intent_id:
raise PermissionError(f"Intent {intent_id} was denied")
raise TimeoutError(f"Intent {intent_id} timed out after 10 minutes")
# --- Usage examples ---
def issue_refund(customer_id: str, amount: float):
permit = require_approval(
action="issue_refund",
risk_tier="high",
parameters={"customer_id": customer_id, "amount": amount},
)
print(f"Permit: {permit[:40]}…")
# ← proceed with actual refund
def delete_account(user_id: str):
permit = require_approval(
action="delete_account",
risk_tier="critical",
parameters={"user_id": user_id},
)
print(f"Permit: {permit[:40]}…")
# ← proceed with account deletion
def read_profile(user_id: str):
# Low-risk — likely auto-approved by policy, no human needed
permit = require_approval(
action="read_profile",
risk_tier="low",
parameters={"user_id": user_id},
)
# ← proceed immediatelyRisk Tier Reference
| Tier | Default behavior | Use for |
|---|---|---|
| low | Creates pending; auto-approve policy recommended | Read operations, status checks, notifications |
| medium | Creates pending; awaits human review | Profile updates, config changes, non-financial writes |
| high | Creates pending; typically requires review | Financial ops, email blasts, data exports |
| critical | Creates pending; requires typed confirmation | Deletions, secret rotation, irreversible changes |
Error Reference
| HTTP | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | API key missing or invalid. Check your Authorization header. |
| 400 | MISSING_FIELDS | action and source_type are required fields. |
| 403 | AGENT_PAUSED | The registered agent matching this fingerprint is paused. Resume it in the dashboard. |
| 403 | AGENT_REVOKED | The registered agent matching this fingerprint has been revoked and cannot submit intents. |
| 404 | NOT_FOUND | Intent ID not found or does not belong to this project. |
| 409 | ALREADY_RESOLVED | Attempted to approve/deny an intent that's already been resolved. |
| 500 | DB_ERROR | Internal error. Retry with exponential backoff. |