# 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
