Quickstart
From signup to your first governed tool call in five minutes.
This walks through wiring Tenet into an existing agent. Python is the primary path, since most agent frameworks (LangGraph, LangChain, CrewAI) are Python, and the TypeScript SDK mirrors the same execute() shape (shown at the bottom).
1. Sign up
Create an account at app.trytenet.com/signup. You'll land on the dashboard with your default protections already active. Use the getting-started banner to create your first API key and fire three test decisions, so you can watch the allow / escalate / block loop before wiring your real agent.
Your key starts with tnt_ and is shown in full exactly once, at creation, so copy it then. If you missed it, create a new key under Keys in the top nav.
2. Install the SDK
pip install tenet-python
3. Wrap a tool call
In your agent code, replace your direct service call with client.execute(). Tenet injects the downstream credentials server-side, so you don't store them in your agent.
from tenet import TenetClient
client = TenetClient(api_key="tnt_...") # or set the TENET_API_KEY env var
decision = client.execute(
service="anthropic",
tool_name="anthropic.messages.create",
arguments={
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello"}],
},
)
if decision.allowed:
print(decision.raw) # the downstream response
elif decision.escalated:
print(f"Held for approval, review {decision.review_id}")
elif decision.blocked:
print(f"Blocked: {decision.reason_codes}")
That's it. Tenet:
- Authenticates the call with your
TENET_API_KEY. - Evaluates the call against your protections: the service's trust level, any per-function overrides, the cost cap, and the built-in safeguards.
- Injects your downstream Anthropic credential server-side (which you'll configure once; see below).
- Calls Anthropic on your behalf, returns the response.
- Writes a decision row to the audit log.
4. Add the downstream credential
Tenet mediates downstream credentials so they never live in your agent code. Connect Anthropic (or whatever services you'll call) once on the Protections page (/protections) — click the service under Available services and paste its key. Tenet injects that credential server-side on every call, so your agent only ever holds its TENET_API_KEY.
Newly connected services start Locked. That means every call — even a read — escalates for your approval until you relax the trust level. It's the safest default: nothing runs behind your back on a fresh credential. Once you've seen a call or two, set the service to Gated (reads and reversible writes allowed; irreversible actions still wait for you) or Trusted on the /protections page. So if your first execute() call returns ESCALATE instead of ALLOW, that's the lock working, not an error: approve it at /reviews or relax the trust level and call again.
The full list of connectors, with the exact tools each exposes and examples, lives on the Connectors page.
5. Make a call that should escalate
Try calling github.repos.delete to see the approval gate in action:
decision = client.execute(
service="github",
tool_name="github.repos.delete",
arguments={"owner": "your-org", "repo": "test-repo"},
)
Because github.repos.delete is classified as irreversible, the call will return an ESCALATE outcome instead of running — at every trust level, even Trusted. A pending review will show up in your dashboard at /reviews. Approve it there → the call executes. Reject → it doesn't.
You'll also get an email notification if you have email_mode set to per_event (the default).
Using LangGraph?
Each tool your agent exposes just calls client.execute() inside its body:
from langchain_core.tools import tool
from tenet import TenetClient
client = TenetClient() # reads TENET_API_KEY from the env
@tool
def delete_repo(owner: str, repo: str) -> str:
"""Delete a GitHub repository."""
d = client.execute(
service="github",
tool_name="github.repos.delete",
arguments={"owner": owner, "repo": repo},
)
if not d.allowed:
return f"Refused by Tenet ({d.outcome.value})."
return "deleted"
TypeScript
The TypeScript SDK mirrors the same execute() surface and is headed to npm. Until it lands there, TypeScript agents can call the REST API directly. It's one POST:
const res = await fetch("https://api.trytenet.com/v1/execute", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TENET_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
service: "anthropic",
toolCall: {
toolName: "anthropic.messages.create",
arguments: { model: "claude-sonnet-4-6", max_tokens: 1024, messages: [] },
},
}),
});
The response carries the same outcome / decisionId / reviewId shape documented in the API reference.
What's next
- The
tenet.execute()reference covers every argument and return shape. - The protections guide explains what's currently enforced.
- The audit log guide explains what we record about every decision.