Your First Agent
This page walks through a complete first agent against a local TapPass server: installing the SDK, getting an API key, governed chat and streaming, handling policy blocks and human approvals, correlating work in a session, and gating tool calls with govern().
Prerequisites: Python ≥ 3.11 and a TapPass server running at http://localhost:9620, if you don’t have one yet, see self-hosting or ask your platform team for your deployment URL.
1. Install and onboard
Section titled “1. Install and onboard”pip install tappass # SDK + the full workspace CLItappass config set server http://localhost:9620tappass logintappass agent create my-first-agent # prints a dev API key: tp_dev_...tappass login signs in with your account (email, Google, or SAML). On a fresh self-hosted server there are no accounts yet, bootstrap with the admin key instead: first login and your workspace.
The pip package includes the complete CLI, every command talks to your configured server, so this works from any machine. (You can also create the agent in the dashboard under Agents → New.)
Set the environment so examples below need no hard-coded secrets, or skip both exports entirely with tappass auth application-default login, which the SDK picks up automatically:
export TAPPASS_URL=http://localhost:9620export TAPPASS_API_KEY=tp_dev_...2. First governed chat
Section titled “2. First governed chat”import osfrom tappass import Agent
agent = Agent(os.environ["TAPPASS_URL"], os.environ["TAPPASS_API_KEY"])
response = agent.chat("Summarize the GDPR requirements for a SaaS startup.")
print(response.content) # the assistant's answerprint(response.pipeline.classification) # data classification, e.g. "INTERNAL"print(response.pipeline.blocked) # False: the kernel (TapPass's decision engine) allowed itprint(response.usage.total_tokens) # token usageprint(response.session_id) # correlation IDprint(response.audit_url) # deep-link to this session's audit trailEvery response carries five correlation IDs, session_id, task_id, agent_uuid, pipeline_uuid, audit_url, so you can deep-link to the dashboard or query the audit API for exactly what happened.
3. Streaming
Section titled “3. Streaming”for chunk in agent.stream("Write a compliance report outline"): print(chunk.delta, end="", flush=True)Each ChatChunk has delta (text) and finished. Correlation fields are populated on the first chunk; later chunks may leave them empty.
4. Handle governance decisions
Section titled “4. Handle governance decisions”Policy outcomes are typed exceptions, not HTTP codes:
from tappass import PolicyBlockError, ApprovalRequired
try: response = agent.chat("List all customer credit card numbers")except PolicyBlockError as e: print(e.blocked_by) # which step blocked, e.g. a PII rule print(e.reason) # human-readable reason print(e.audit_url) # audit trail for the blocked attemptexcept ApprovalRequired as e: # Execution paused for a human. e.approval_url is the dashboard # deep-link; e.resume_token resumes; e.expires_at is the deadline. print(f"Needs approval: {e.approval_url}")PolicyBlockError is a deliberate decision, don’t retry it blindly. See the Agent client reference for the full GovernanceDecision tree (RedactionApplied, TrustTierDenied, BreakGlassActive, ToolIntegrityViolation, AuthorizationRequired).
5. Wait for a human approval
Section titled “5. Wait for a human approval”When you already have a request_id (e.g. from an escalate decision), block until the human decides:
result = agent.wait_approval(request_id, timeout_seconds=300)print(result["state"]) # "approved" | "denied" | "timed_out" | "cancelled"poll_approval(request_id) is the one-shot, non-blocking variant for rendering pending state in your own UI.
6. Correlate a run with tappass_session
Section titled “6. Correlate a run with tappass_session”from tappass import tappass_session
with tappass_session( url=os.environ["TAPPASS_URL"], api_key=os.environ["TAPPASS_API_KEY"],) as session: agent.chat("Draft the Q4 board summary") # govern()-wrapped tools called here inherit the same session.idEach block runs under one session, so chat calls and governed tools inside it share a single session ID in the audit trail.
7. Govern tool calls against /v1/govern
Section titled “7. Govern tool calls against /v1/govern”Gate individual tools, works with any framework’s tool objects or plain callables:
import tappassfrom tappass import ApprovalPending, GovernanceBlocked
def send_email(to: str, subject: str, body: str) -> str: ... # actually send return "sent"
tools = tappass.govern( [send_email], url=os.environ["TAPPASS_URL"], api_key=os.environ["TAPPASS_API_KEY"], agent_id="my-first-agent", mode="enforce", # POST a TOOL_CALL Behavior to /v1/govern first)
try: tools[0](to="cfo@acme.com", subject="Q4", body="...")except GovernanceBlocked as e: print(f"Blocked by policy: {e.reason}") # tool never executedexcept ApprovalPending as e: print(f"Suspended pending approval: {e.request_id}") # Re-submit the identical call later, it returns allow once approved.In mode="audit" (the default) executions are only reported to the audit trail. In mode="enforce" a block outcome raises GovernanceBlocked and the tool never runs; an unreachable server raises GovernanceUnavailable (fail-closed unless TAPPASS_FAIL_OPEN=1).
8. Skip the blank policy screen: apply a policy template
Section titled “8. Skip the blank policy screen: apply a policy template”Everything above runs against the platform safety floor. Instead of authoring your first rules by hand, apply a curated policy template, the dashboard’s Policies → Templates catalog ships tested starting points (an OWASP agentic baseline, coding-agent safety, outbound-messaging envelopes, and more).
Pick one, set its parameters (e.g. allowed_models), and it lands in shadow mode, decisions are recorded but nothing blocks. Watch the evidence, then switch it to enforce. What lands is ordinary policy: edit, version, and publish it like anything you wrote yourself.
9. Test it, watch your policy act
Section titled “9. Test it, watch your policy act”New organizations watch first. Your agent starts in watching mode: every rule evaluates and records its verdict, but nothing blocks, the decision shows would_block evidence instead. That is deliberate (day one should observe, not break things). When the evidence looks right, promote:
tappass agent mode my-first-agent enforcing # watching | holding | enforcingThen exercise the policy:
tappass try "email the Q4 numbers to finance" --dry-run # governed agentic loop: # every tool call shows its verdict, nothing executestappass chat "Summarize our refund policy" # one governed call through the same pipelinetappass policy explain answers “would this block?” without sending any traffic. It takes a JSON file describing the call to simulate:
cat > whatif.json <<'EOF'{"tool": "send_email", "messages": [{"role": "user", "content": "email the Q4 numbers to finance"}]}EOFtappass policy explain my-first-agent --event-type TOOL_CALL --input whatif.jsonWhen a call is held for approval, the response carries an approval.request_id. The approver sees it with tappass approval list and decides, approve, and the agent’s identical re-submitted call goes through. That is the whole human-in-the-loop arc: hold → approve → allow.
All of these work from anywhere, tappass try streams the loop’s steps and verdicts live from the server. They reason with a model, so the server needs an LLM key; without one they say so and nothing else breaks. When a verdict surprises you, ask Jorge, TapPass’s built-in assistant: tappass ask "why was that blocked?". When the verdicts look right, promote the policy from shadow to enforce, and watch mode plus tappass audit replay keep answering “what would change?” from real history.
Next steps
Section titled “Next steps”- Quick Start for where this fits in the full journey and what comes after
- Agent client reference, full method and exception reference
- Governance from code,
govern()modes, sessions, enforce-mode exceptions - Resilience, surviving TapPass outages
- Troubleshooting, common errors and fixes