From install to verified execution.
Install the SDK, create authority, move credentials behind AgentKey, execute through a single-use capability, then verify the evidence. No decorative demo path.
What AgentKey does
AgentKey evaluates every tool call your AI agent wants to make against a stored policy, returns ALLOW, DENY, or approval required, and records verifiable evidence of the decision and of what actually ran. The default is deny: anything you have not explicitly allowed does not execute, and if the service cannot answer, the SDK fails closed.
Install the SDK
Zero dependencies. Node 18+ and Python 3.8+. Install the package, then run the one-command setup.
npm install agentkey-ai npx agentkey setup # or Python pip install -U agentkey agentkey setup
Configure the client
Both SDKs default to the production API. The key resolves from an explicit argument, then AGENTKEY_API_KEY, then the local config written by `agentkey setup` / `agentkey init` (~/.agentkey/config, 0600).
// JavaScript / TypeScript
import { AgentKeyClient } from "agentkey-ai";
const ak = new AgentKeyClient({ apiKey: process.env.AGENTKEY_API_KEY });
# Python
import os
from agentkey import AgentKeyClient
ak = AgentKeyClient(api_key=os.environ["AGENTKEY_API_KEY"])Create your agent and an API key
This is the first step that needs an account, and the only reason: API keys are issued per agent under your account, so your policies and evidence chain stay isolated to you and nobody else can act as your agent. Pick or create an agent in the Connect wizard and create a key - it is shown exactly once; store it as AGENTKEY_API_KEY and never commit it.
First authorization (guided)
Copy, paste, run. Every new agent ships with exactly one isolated test permission - sandbox.send_test - that can only ever return allow or deny and never performs a real side effect. This single call produces a real policy-engine decision and a verifiable evidence event in your dashboard.
# Python - save as first_authorization.py, then: python first_authorization.py
from agentkey import AgentKeyClient
ak = AgentKeyClient() # reads AGENTKEY_API_KEY (or the config file from agentkey init)
result = ak.check_permission(action="send_test", resource="sandbox")
print("session_id: ", result.get("session_id"))
print("decision: ", "ALLOW" if result.get("allowed") else "DENY", "-", result.get("reason"))
if result.get("event_id"):
print("evidence_event_id:", result["event_id"])
print("YOU'RE DONE - your first real authorization is recorded. See it:")
print("https://agentkey.us/sessions/" + str(result.get("session_id")))
else:
reason = str(result.get("reason") or "")
if "Invalid API key" in reason:
print("Key invalid - create one at https://agentkey.us/connect")
elif "No permission configured" in reason:
print("Add a permission in the dashboard: resource 'sandbox',")
print("action 'send_test', decision allow - then run this again.")
else:
print("Denied by policy:", reason)
// JavaScript / TypeScript - save as first-authorization.mjs, then: node first-authorization.mjs
import { AgentKeyClient } from "agentkey-ai";
const ak = new AgentKeyClient(); // reads AGENTKEY_API_KEY (or the config file from npx agentkey init)
const result = await ak.checkPermission({ action: "send_test", resource: "sandbox" });
console.log("session_id: ", result.session_id);
console.log("decision: ", result.allowed ? "ALLOW" : "DENY", "-", result.reason);
if (result.event_id) {
console.log("evidence_event_id:", result.event_id);
console.log("YOU'RE DONE - your first real authorization is recorded. See it:");
console.log("https://agentkey.us/sessions/" + result.session_id);
} else {
const reason = String(result.reason || "");
if (reason.includes("Invalid API key")) {
console.log("Key invalid - create one at https://agentkey.us/connect");
} else if (reason.includes("No permission configured")) {
console.log("Add a permission in the dashboard: resource 'sandbox',");
console.log("action 'send_test', decision allow - then run this again.");
} else {
console.log("Denied by policy:", reason);
}
}Define the policy
A permission matches a resource + action pair and carries a decision (allow / ask / deny), optionally constrained by parameter conditions. No permission configured means the default is deny.
{
"resource": "stripe.charge",
"action": "invoke",
"decision": "allow",
"conditions": [
{ "field": "amount", "operator": "lte", "value": "100", "on_fail": "deny" }
]
}Evaluate a tool call
checkPermission is the core call. The three possible outcomes: ALLOW runs the action, DENY blocks it, and 'ask' pauses for a human approval in the dashboard. On any error or timeout the SDK fails closed - allowed: false.
const decision = await ak.checkPermission({
action: "invoke",
resource: "stripe.charge",
arguments: { amount: 150 },
});
// ALLOW: { allowed: true, reason: "...", event_id: "evt_..." }
// DENY: { allowed: false, reason: "denied by policy" }
// APPROVAL REQUIRED: { allowed: false, approval_required: true }
// → a human approves or denies it in the dashboardMove credentials behind AgentKey
For the strongest boundary, configure a Brokered Target. Your agent keeps a target_id, not the provider credential. AgentKey verifies the execution contract, consumes the one-time capability, injects the credential server-side, executes the request, and can hash real before/after state.
// JavaScript
const stripe = ak.stripe("target_id_from_dashboard");
const result = await stripe.execute({
action: "retrieve_payment_intent",
resource: "stripe",
sessionId: s.session_id,
arguments: {
path: "/v1/payment_intents/{payment_intent_id}",
payment_intent_id: "pi_...",
method: "GET"
}
});
console.log(ak.explain(result));
# Python
stripe = ak.stripe("target_id_from_dashboard")
result = stripe.execute(
action="retrieve_payment_intent",
resource="stripe",
session_id=s["session_id"],
arguments={"path": "/v1/payment_intents/{payment_intent_id}", "payment_intent_id": "pi_...", "method": "GET"},
)
print(ak.explain(result))Generate evidence
wrap() instruments an existing agent in one line - every tool call becomes paired authorization + execution evidence on a hash-chained session, with no call-site changes. Observe mode (default) records and blocks nothing; enforce mode raises on denials.
const agent = ak.wrap(myAgent); // observe mode (default)
// const agent = ak.wrap(myAgent, { mode: "enforce" });
// Or thread decisions into an explicit session:
const s = await ak.startSession({ metadata: { task: "daily-report" } });
await ak.checkPermission({
action: "invoke",
resource: "email.send",
sessionId: s.session_id,
});
await ak.endSession({ sessionId: s.session_id });Verify the evidence
Verification recomputes the chain from the stored raw events - it never trusts the stored root. Session-level signatures (HMAC-SHA256, plus Ed25519 when enabled) are produced at session close. Run it months later, from outside AgentKey.
const report = await ak.verifySession({ sessionId: s.session_id });
// report.integrity.status:
// "VERIFIED" | "VERIFIED WITH GAPS" | "INVALID" | "INCOMPLETE"
// Python: ak.verify_session(session_id)Troubleshooting
The common cases, and what each one means.
# allowed: false, reason "agentkey_unreachable" # → fail-closed by design: network error or timeout. The agent never # proceeds on a missing decision. Check connectivity and AGENTKEY_BASE_URL. # HTTP 403 / "Cloudflare error 1010" # → blocked by the edge WAF, not an auth failure. A proxy or wrapper is # likely stripping the SDK's User-Agent header - restore it. # "no API key configured" # → export AGENTKEY_API_KEY=... or run: agentkey init # HTTP 429 # → per-agent rate limit. Retry after the indicated interval.