SDK Integration
# SDK Integration
Self Agent ID provides three SDKs with identical feature parity:
| Language | Package | Install |
|----------|---------|---------|
| TypeScript | `@selfxyz/agent-sdk` | `npm install @selfxyz/agent-sdk` |
| Python | `selfxyz-agent-sdk` | `pip install selfxyz-agent-sdk` |
| Rust | `self-agent-sdk` | `cargo add self-agent-sdk` |
## Agent-Side: Signing Requests
Use `SelfAgent` to sign outbound API requests. The SDK attaches three headers to every request:
- `x-self-agent-address` — the agent's Ethereum address
- `x-self-agent-signature` — ECDSA signature of `keccak256(timestamp + METHOD + path + bodyHash)`
- `x-self-agent-timestamp` — Unix timestamp (seconds)
::::tabs
:::tab{label="TypeScript"}
```typescript
import { SelfAgent } from "@selfxyz/agent-sdk";
const agent = new SelfAgent({
privateKey: process.env.AGENT_PRIVATE_KEY!,
registryAddress: "0xaC3DF9ABf80d0F5c020C06B04Cced27763355944",
rpcUrl: "https://forno.celo.org",
});
// Signed fetch — headers are attached automatically
const res = await agent.fetch("https://api.example.com/protected", {
method: "POST",
body: JSON.stringify({ action: "hello" }),
});
```
:::
:::tab{label="Python"}
```python
from self_agent_sdk import SelfAgent
agent = SelfAgent(
private_key=os.environ["AGENT_PRIVATE_KEY"],
registry_address="0xaC3DF9ABf80d0F5c020C06B04Cced27763355944",
rpc_url="https://forno.celo.org",
)
# Signed request
res = agent.fetch("https://api.example.com/protected", method="POST",
json={"action": "hello"})
```
:::
:::tab{label="Rust"}
```rust
use self_agent_sdk::SelfAgent;
let agent = SelfAgent::new(
&std::env::var("AGENT_PRIVATE_KEY")?,
"0xaC3DF9ABf80d0F5c020C06B04Cced27763355944",
"https://forno.celo.org",
)?;
let res = agent.fetch("https://api.example.com/protected")
.method("POST")
.json(&serde_json::json!({"action": "hello"}))
.send()
.await?;
```
:::
::::
## Service-Side: Verifying Requests
Use `SelfAgentVerifier` to verify incoming agent requests. The verifier recovers the signer address from the ECDSA signature, derives the agent key, and checks `isVerifiedAgent()` on-chain.
### Builder Pattern
::::tabs
:::tab{label="TypeScript"}
```typescript
import { SelfAgentVerifier } from "@selfxyz/agent-sdk";
const verifier = SelfAgentVerifier.create()
.requireAge(18)
.requireOFAC()
.maxAgentsPerHuman(1)
.build();
// Express middleware
app.use("/api", verifier.auth());
```
:::
:::tab{label="Python"}
```python
from self_agent_sdk import SelfAgentVerifier
verifier = (SelfAgentVerifier.create()
.require_age(18)
.require_ofac()
.max_agents_per_human(1)
.build())
# Flask middleware
@app.before_request
def verify_agent():
result = verifier.verify_request(request)
if not result.valid:
return {"error": result.error}, 401
```
:::
:::tab{label="Rust"}
```rust
use self_agent_sdk::SelfAgentVerifier;
let verifier = SelfAgentVerifier::builder()
.require_age(18)
.require_ofac()
.max_agents_per_human(1)
.build()?;
// Axum middleware (requires `axum` feature)
let app = Router::new()
.route("/api/protected", post(handler))
.layer(verifier.into_layer());
```
:::
::::
### Direct Verification
```typescript
const result = await verifier.verify({
signature: req.headers["x-self-agent-signature"],
timestamp: req.headers["x-self-agent-timestamp"],
method: req.method,
url: req.url,
body: req.body,
});
if (result.valid) {
console.log(`Verified agent: ${result.agentAddress}, ID #${result.agentId}`);
console.log(`Credentials:`, result.credentials);
}
```
## Security Defaults
`SelfAgentVerifier` defaults are strict:
| Setting | Default | Description |
|---------|---------|-------------|
| `requireSelfProvider` | `true` | Only accept proofs from Self Protocol's provider |
| `maxAgentsPerHuman` | `1` | One agent per human (sybil resistance) |
| Replay protection | Enabled | Signature nonce + timestamp freshness |
| Timestamp window | 300 seconds | Reject requests older than 5 minutes |
:::warning
Setting `requireSelfProvider: false` accepts verification from any approved proof provider, not only Self Protocol.
:::
## Agent Status & Info
```typescript
// Check registration status
const registered = await agent.isRegistered();
// Get full agent info
const info = await agent.getInfo();
// { agentId, address, agentKey, isVerified, proofProvider, proofExpiresAt }
```
## Proof Expiry
Agent proofs have a validity window. The SDK exposes expiry information and handles stale proofs:
```typescript
const info = await agent.getInfo();
const expiresAt = info.proofExpiresAt; // Unix timestamp (seconds)
// SDK verifiers automatically reject expired proofs
// 30-day warning threshold for upcoming expiry
```
:::warning
When a proof expires, the agent's `isProofFresh()` returns `false` on-chain. There is no on-chain refresh mechanism — the agent must deregister and re-register with a fresh proof (scanning their passport again). The soulbound NFT is NOT burned — it remains as a historical record.
:::
## A2A Agent Cards
Build and store standardized agent identity cards:
```typescript
import { buildAgentCard } from "@selfxyz/agent-sdk";
const card = await buildAgentCard(agent, {
name: "My Agent",
description: "A human-verified AI assistant",
});
// Store on-chain via updateAgentMetadata()
```
Cards follow the A2A format and are queryable via:
- `GET /api/cards/{chainId}/{agentId}`
- `GET /.well-known/a2a/{agentId}?chain={chainId}`
- `POST /api/a2a` — A2A JSON-RPC endpoint for agent-to-agent communication
Self Agent ID provides three SDKs with identical feature parity:
| Language | Package | Install |
|---|---|---|
| TypeScript | @selfxyz/agent-sdk | npm install @selfxyz/agent-sdk |
| Python | selfxyz-agent-sdk | pip install selfxyz-agent-sdk |
| Rust | self-agent-sdk | cargo add self-agent-sdk |
Agent-Side: Signing Requests
Use SelfAgent to sign outbound API requests. The SDK attaches three headers to every request:
x-self-agent-address— the agent’s Ethereum addressx-self-agent-signature— ECDSA signature ofkeccak256(timestamp + METHOD + path + bodyHash)x-self-agent-timestamp— Unix timestamp (seconds)
import { SelfAgent } from "@selfxyz/agent-sdk";
const agent = new SelfAgent({
privateKey: process.env.AGENT_PRIVATE_KEY!,
registryAddress: "0xaC3DF9ABf80d0F5c020C06B04Cced27763355944",
rpcUrl: "https://forno.celo.org",
});
// Signed fetch — headers are attached automatically
const res = await agent.fetch("https://api.example.com/protected", {
method: "POST",
body: JSON.stringify({ action: "hello" }),
});from self_agent_sdk import SelfAgent
agent = SelfAgent(
private_key=os.environ["AGENT_PRIVATE_KEY"],
registry_address="0xaC3DF9ABf80d0F5c020C06B04Cced27763355944",
rpc_url="https://forno.celo.org",
)
# Signed request
res = agent.fetch("https://api.example.com/protected", method="POST",
json={"action": "hello"})use self_agent_sdk::SelfAgent;
let agent = SelfAgent::new(
&std::env::var("AGENT_PRIVATE_KEY")?,
"0xaC3DF9ABf80d0F5c020C06B04Cced27763355944",
"https://forno.celo.org",
)?;
let res = agent.fetch("https://api.example.com/protected")
.method("POST")
.json(&serde_json::json!({"action": "hello"}))
.send()
.await?;Service-Side: Verifying Requests
Use SelfAgentVerifier to verify incoming agent requests. The verifier recovers the signer address from the ECDSA signature, derives the agent key, and checks isVerifiedAgent() on-chain.
Builder Pattern
import { SelfAgentVerifier } from "@selfxyz/agent-sdk";
const verifier = SelfAgentVerifier.create()
.requireAge(18)
.requireOFAC()
.maxAgentsPerHuman(1)
.build();
// Express middleware
app.use("/api", verifier.auth());from self_agent_sdk import SelfAgentVerifier
verifier = (SelfAgentVerifier.create()
.require_age(18)
.require_ofac()
.max_agents_per_human(1)
.build())
# Flask middleware
@app.before_request
def verify_agent():
result = verifier.verify_request(request)
if not result.valid:
return {"error": result.error}, 401use self_agent_sdk::SelfAgentVerifier;
let verifier = SelfAgentVerifier::builder()
.require_age(18)
.require_ofac()
.max_agents_per_human(1)
.build()?;
// Axum middleware (requires `axum` feature)
let app = Router::new()
.route("/api/protected", post(handler))
.layer(verifier.into_layer());Direct Verification
const result = await verifier.verify({
signature: req.headers["x-self-agent-signature"],
timestamp: req.headers["x-self-agent-timestamp"],
method: req.method,
url: req.url,
body: req.body,
});
if (result.valid) {
console.log(`Verified agent: ${result.agentAddress}, ID #${result.agentId}`);
console.log(`Credentials:`, result.credentials);
}
Security Defaults
SelfAgentVerifier defaults are strict:
| Setting | Default | Description |
|---|---|---|
requireSelfProvider | true | Only accept proofs from Self Protocol’s provider |
maxAgentsPerHuman | 1 | One agent per human (sybil resistance) |
| Replay protection | Enabled | Signature nonce + timestamp freshness |
| Timestamp window | 300 seconds | Reject requests older than 5 minutes |
Agent Status & Info
// Check registration status
const registered = await agent.isRegistered();
// Get full agent info
const info = await agent.getInfo();
// { agentId, address, agentKey, isVerified, proofProvider, proofExpiresAt }
Proof Expiry
Agent proofs have a validity window. The SDK exposes expiry information and handles stale proofs:
const info = await agent.getInfo();
const expiresAt = info.proofExpiresAt; // Unix timestamp (seconds)
// SDK verifiers automatically reject expired proofs
// 30-day warning threshold for upcoming expiry
A2A Agent Cards
Build and store standardized agent identity cards:
import { buildAgentCard } from "@selfxyz/agent-sdk";
const card = await buildAgentCard(agent, {
name: "My Agent",
description: "A human-verified AI assistant",
});
// Store on-chain via updateAgentMetadata()
Cards follow the A2A format and are queryable via:
GET /api/cards/{chainId}/{agentId}GET /.well-known/a2a/{agentId}?chain={chainId}POST /api/a2a— A2A JSON-RPC endpoint for agent-to-agent communication