What We’re Building
An agent that connects to an ATH-protected service, proves its identity, gets user approval, and calls the service’s API.Before You Start
What you need
| Requirement | Why | Details |
|---|---|---|
| An ATH-protected service to connect to | Your agent needs something to talk to | Use the demo, or any ATH-enabled service/gateway |
| Node.js 20+ or Python 3.10+ | To run the SDK | Or use the athx CLI (Node.js only) |
| An ES256 key pair | To prove your agent’s identity. Every ATH request includes a signed proof (“I am this agent”) created with your private key. | The SDK can generate one for you. See Step 1 below. |
What about agentId — do I need a public URL?
The agentId is a URL that points to your agent’s identity document — a JSON file containing your agent’s name, developer info, and public key. In production, the server fetches this URL to get your public key and verify your identity proofs.
| Scenario | Do you need a hosted agentId? | What to do |
|---|---|---|
Development (server has skipAttestationVerification) | No | Use any URL as a placeholder. The server won’t actually fetch it. |
| Gateway mode (production) | Depends on gateway config | Many gateways trust registered agents without fetching the identity document. |
| Native mode (production) | Yes | Host agent.json at a URL the server can reach. See Step 1. |
Step 1: Set Up Your Agent Identity
Every agent in ATH has an identity — a key pair and a document that ties them together. Think of it like an ID card: the document is the card, and the private key proves you’re the person on the card.Generate an ES256 Key Pair
- TypeScript
- Python
- OpenSSL (any language)
What is ES256?
What is ES256?
ES256 is a digital signature algorithm using the P-256 elliptic curve. It creates two keys:
- Private key — kept secret by your agent. Used to sign identity proofs.
- Public key — shared openly. Used by servers to verify your proofs.
Create Your Identity Document
This JSON file tells servers who your agent is and how to verify its identity:| Field | What it’s for |
|---|---|
agent_id | The URL where this document is hosted. Servers fetch this URL to get your public key. |
name | Human-readable name shown to users on consent screens |
developer | Who built this agent — helps service operators evaluate trust |
capabilities | What this agent does — informational, not enforced |
public_key | Your public key in JWK format. Servers use this to verify your identity proofs. |
Host the Identity Document (Production Only)
In production, host this JSON file at the URL specified inagent_id. For example, if your agent_id is https://your-agent.com/.well-known/agent.json, the file must be fetchable at that URL.
The demo includes a minimal identity server (agent/server.ts) that does this — it’s just an Express server with one route.
I'm in development — do I need to host this?
I'm in development — do I need to host this?
No. If the server you’re connecting to has
skipAttestationVerification: true (like the demo), it won’t fetch your identity document. You still need to provide an agentId string when creating the client, but it can be any URL — the server just stores it as a label, without fetching it.In gateway mode, many gateways also skip or cache identity verification, so you may not need a reachable URL even in production. Check with your gateway operator.Step 2: Connect to a Service
Now use your key pair to create an ATH client and walk through the protocol.What the SDK Does For You
When you callregister(), authorize(), or exchangeToken(), the SDK automatically:
- Creates a fresh attestation JWT (identity proof) containing your
agentId, a timestamp, and a unique ID - Signs it with your private key
- Includes it in the HTTP request to the server
agent_id URL (or skips this in development), and verifies the signature. If it matches, the server knows the request really came from your agent.
You never need to build or sign JWTs yourself — the SDK handles it all.
The Full Flow
- TypeScript SDK
- Python SDK
- athx CLI
Understanding Key Concepts
The Attestation JWT (Identity Proof)
Every time the SDK calls register, authorize, or token, it creates and signs a JWT that looks like this:| Field | What it says |
|---|---|
iss | ”I was created by the agent at this domain” |
sub | ”My identity document is at this URL” |
aud | ”I’m talking to this specific server” (prevents the proof from being replayed to a different server) |
iat | ”I created this proof at this time” (must be within 5 minutes of server time) |
exp | ”This proof expires at this time” |
jti | ”This is a unique proof” (prevents the same proof from being used twice) |
INVALID_ATTESTATION.
The User Consent Step
Step 3 (Authorize) returns a URL. This is the most confusing part for beginners, so here’s exactly what happens: Key points:- Your agent doesn’t handle the OAuth redirect. The service/gateway handles the callback.
- Your agent just waits. After getting the
authorization_url, show it to the user and wait. - The session expires in 10 minutes. If the user doesn’t approve in time, call
authorizeagain. - You pass the
ath_session_idto token exchange — this is how the server knows which consent to match with your token request.
Gateway Mode vs Native Mode
| Gateway | Native | |
|---|---|---|
| Connect to | ATHGatewayClient / --gateway | ATHNativeClient / --mode native |
| API calls via | proxy(provider, method, path) | api(method, path) |
| Agent needs public URL? | No (in most configs) | Yes (production with verification) |
| Use when | Connecting through a gateway to external services | Connecting directly to an ATH-native service |
Common Errors and What They Mean
| Error | Most likely cause | Fix |
|---|---|---|
INVALID_ATTESTATION | Attestation JWT is expired, has wrong audience, or was already used | The SDK creates fresh JWTs automatically — check that your system clock is accurate and that the server URL matches your config |
AGENT_NOT_REGISTERED | You called authorize or token before register | Call register() first |
SCOPE_NOT_APPROVED | You requested scopes the service didn’t approve | Check reg.approved_providers to see what was actually approved |
SESSION_EXPIRED | More than 10 minutes passed between authorize and token | Call authorize() again to get a fresh session |
TOKEN_EXPIRED | Token’s expires_in has elapsed (default: 1 hour) | Call authorize() → user approves → exchangeToken() again |
PROVIDER_MISMATCH | Token was issued for a different provider | Each token works for exactly one provider |
Try It With the Demo
Run the ATH Demo to test against a real e-commerce service:Next Steps
- TypeScript SDK reference — full API, error handling patterns
- Python SDK reference — sync/async, credential persistence
- athx CLI reference — all commands, scripting with JSON output
- LangChain integration — wrap ATH APIs as LangChain tools
- Claude Code integration — use athx from Claude Code agents