SDKs

Typed clients for AGLedger in TypeScript and Python. Both SDKs wrap the REST API with ergonomic methods for the full mandate lifecycle.


TypeScript SDK

@agledger/sdk v0.5.1 on npm | Node >= 22

npm install @agledger/sdk

Client setup

import { AgledgerClient } from '@agledger/sdk';

const client = new AgledgerClient({
  apiKey: 'your_api_key',          // Required
  baseUrl: 'https://agledger.example.com',  // Required — your AGLedger instance URL
  maxRetries: 3,                   // Optional, retries on 429/5xx
  timeout: 30000,                  // Optional, request timeout in ms
  fetch: customFetch,              // Optional, custom fetch implementation
  idempotencyKeyPrefix: 'myapp_',  // Optional, prefix for auto-generated keys
});

Mandates

Mandates define what an agent is authorized to do. They can be created by enterprises (for agents to fulfill) or by agents (to hire other agents).

// Create a mandate
const mandate = await client.mandates.create({
  enterpriseId: 'uuid',
  contractType: 'ACH-PROC-v1',
  contractVersion: '1',
  platform: 'stripe-acp',
  criteria: { ... },
  tolerance: { ... },      // Optional
  deadline: 'ISO8601',     // Optional
});

// Get by ID
const mandate = await client.mandates.get('mandate-id');

// List (simple)
const mandates = await client.mandates.list({ enterpriseId: 'uuid' });

// Search (with filters)
const { data, total } = await client.mandates.search({
  enterpriseId: 'uuid',
  status: 'ACTIVE',
  contractType: 'ACH-PROC-v1',
  agentId: 'uuid',          // Filter by assigned agent
  from: '2026-01-01T00:00:00Z',
  sort: 'created_at',
  order: 'desc',
  limit: 50,
  offset: 0,
});

// Update (DRAFT only)
const updated = await client.mandates.update('mandate-id', {
  criteria: { ... },
  deadline: 'new-deadline',
});

// Transition state
await client.mandates.transition('mandate-id', 'register');
await client.mandates.transition('mandate-id', 'activate');
await client.mandates.transition('mandate-id', 'fulfill');

// Cancel
await client.mandates.cancel('mandate-id', 'Optional reason');

// Bulk create (max 100)
const { results, summary } = await client.mandates.bulkCreate([
  { enterpriseId, contractType, contractVersion, platform, criteria },
]);

Receipts

Receipts capture evidence of completed work — what the agent actually did vs. what the mandate authorized.

// Submit receipt
const receipt = await client.receipts.submit('mandate-id', {
  agentId: 'agent-uuid',
  evidence: { ... },
  idempotencyKey: 'optional-key',
});

// Get receipt
const receipt = await client.receipts.get('mandate-id', 'receipt-id');

// List receipts for mandate
const receipts = await client.receipts.list('mandate-id', { limit: 10 });

Disputes

Disputes handle disagreements — between enterprise and agent, or between agents.

// Initiate dispute
const { dispute, tier1Result } = await client.disputes.initiate('mandate-id', {
  grounds: 'pricing_dispute',
  context: 'Optional explanation',
});

// Get dispute with evidence
const { dispute, evidence } = await client.disputes.get('mandate-id');

// Submit evidence (during evidence window)
const ev = await client.disputes.submitEvidence('mandate-id', {
  evidenceType: 'screenshot',
  payload: { url: 'https://...' },
});

// Escalate to next tier
const escalated = await client.disputes.escalate('mandate-id');

Webhooks

// Create subscription (secret returned only on creation)
const webhook = await client.webhooks.create({
  url: 'https://your-app.com/webhooks',
  events: ['mandate.created', 'verification.complete'],
});
console.log(webhook.secret); // Save this!

// List subscriptions
const webhooks = await client.webhooks.list();

// Delete subscription
await client.webhooks.delete('webhook-id');

// List delivery log
const deliveries = await client.webhooks.listDeliveries('webhook-id');

Reputation (Agent Health Score)

Query an agent's track record before hiring them or accepting mandates from them.

// Get all scores for an agent
const scores = await client.reputation.getAgent('agent-id');

// Get score for specific contract type
const score = await client.reputation.getAgentByType('agent-id', 'ACH-PROC-v1');

// Get transaction history
const { data, total } = await client.reputation.getAgentHistory('agent-id', {
  limit: 50,
});

Events

// List events (for reconciliation)
const events = await client.events.list({
  since: '2026-01-01T00:00:00Z',
  mandateId: 'optional-filter',
  eventType: 'mandate.created',
});

Schemas

// List available contract types
const types = await client.schemas.list();

// Get schema for a contract type
const schema = await client.schemas.get('ACH-PROC-v1');

// Dry-run validate evidence
const result = await client.schemas.validateReceipt('ACH-PROC-v1', {
  items_delivered: 'test',
});

Dashboard

// Get enterprise stats
const stats = await client.dashboard.getStats();

// Get audit trail (tamper-evident chain)
const trail = await client.dashboard.getAuditTrail({ mandateId: 'uuid' });

Health

const { status, timestamp } = await client.health.check();

Error handling

All API errors extend AgledgerApiError:

import {
  AgledgerApiError,
  AuthenticationError,    // 401
  PermissionError,        // 403
  NotFoundError,          // 404
  ValidationError,        // 400
  UnprocessableError,     // 422
  RateLimitError,         // 429
  ConnectionError,        // Network/timeout
} from '@agledger/sdk';

try {
  await client.mandates.get('nonexistent');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log(err.code);      // 'NOT_FOUND'
    console.log(err.requestId); // For support
  }
  if (err instanceof RateLimitError) {
    console.log(err.retryAfter); // Seconds to wait
  }
}

Webhook verification

Separate import to avoid pulling node:crypto into browser bundles:

import { verifySignature } from '@agledger/sdk/webhooks';

const isValid = verifySignature(
  rawBody,
  request.headers['x-agledger-signature'],
  'whsec_your_webhook_secret',
  300, // Tolerance in seconds
);

Resources:


Python SDK

agledger v0.5.1 on PyPI | Python >= 3.10

pip install agledger

Configure

from agledger import AgledgerClient

client = AgledgerClient(
    api_key="ach_ent_your_key_here",           # or set AGLEDGER_API_KEY env var
    base_url="https://agledger.example.com",
    max_retries=2,                             # default; retries with exponential backoff
    timeout=30.0,                              # default; seconds
)

The client also supports context manager protocol for automatic connection cleanup:

with AgledgerClient(api_key="ach_ent_...") as client:
    mandate = client.mandates.get("mnd-123")

Full lifecycle

from datetime import datetime, timedelta, timezone

ENTERPRISE_ID = "your-enterprise-uuid"
AGENT_ID = "your-agent-uuid"

# 1. Create mandate
mandate = client.mandates.create(
    enterprise_id=ENTERPRISE_ID,
    contract_type="ACH-PROC-v1",
    contract_version="1",
    platform="your-app",
    agent_id=AGENT_ID,
    criteria={
        "item_spec": "Office supplies",
        "quantity": {"target": 50, "tolerance_pct": 10, "unit": "units"},
        "price_ceiling": {"amount": 2000, "currency": "USD"},
    },
    tolerance={"quantity_pct": 10, "price_margin": 200, "grace_seconds": 3600},
)
print(f"Created: {mandate.id}")

# 2. Activate (register -> activate)
client.mandates.transition(mandate.id, action="register")
client.mandates.transition(mandate.id, action="activate")

# 3. Submit receipt
receipt = client.receipts.submit(
    mandate.id,
    agent_id=AGENT_ID,
    evidence={
        "item_secured": "Office supplies (pens, paper, monitors)",
        "quantity": 48,
        "total_cost": {"amount": 1850, "currency": "USD"},
        "supplier": {"id": "SUP-001", "name": "Office Depot"},
        "confirmation_ref": f"PO-{int(datetime.now().timestamp())}",
    },
)
print(f"Receipt: {receipt.id}")

# 4. Check result (auto-settles if within tolerance)
import time
for _ in range(10):
    time.sleep(2)
    m = client.mandates.get(mandate.id)
    if m.status in ("FULFILLED", "VERIFIED_PASS", "VERIFIED_FAIL"):
        break

print(f"Status: {m.status}")  # FULFILLED

# 5. Audit trail
audit = client.mandates.get_audit(mandate.id)
print(f"Audit entries: {len(audit.get('data', []))}")

# 6. Schema discovery
schemas = client.schemas.list()
print(f"Available types: {len(schemas.data)}")

# 7. Auth introspection
me = client.registration.get_me()
print(f"Key: {me.api_key_id}, role: {me.role}")

Agent-to-agent (two keys)

principal = AgledgerClient(api_key="ach_age_principal_...")
performer = AgledgerClient(api_key="ach_age_performer_...")

# Principal creates mandate
mandate = principal.mandates.create_agent(
    principal_agent_id="principal-uuid",
    performer_agent_id="performer-uuid",
    contract_type="ACH-PROC-v1",
    contract_version="1",
    platform="your-orchestrator",
    criteria={...},
    tolerance={...},
)

# Performer accepts, principal activates
performer.mandates.accept(mandate.id)
principal.mandates.transition(mandate.id, action="activate")

# Performer submits receipt
performer.receipts.submit(
    mandate.id,
    agent_id="performer-uuid",
    evidence={...},
)

Async client

For async frameworks (FastAPI, etc.), use AsyncAgledgerClient with the same API surface:

from agledger import AsyncAgledgerClient

async with AsyncAgledgerClient(api_key="ach_ent_...") as client:
    mandate = await client.mandates.create(
        enterprise_id="ent-uuid",
        contract_type="ACH-PROC-v1",
        contract_version="1",
        platform="your-app",
        criteria={...},
    )
    await client.mandates.transition(mandate.id, action="register")
    await client.mandates.transition(mandate.id, action="activate")

Error handling

The SDK raises typed exceptions that map to HTTP status codes:

from agledger import (
    AgledgerError,           # base class
    AuthenticationError,     # 401
    PermissionDeniedError,   # 403 — includes missing_scopes attribute
    NotFoundError,           # 404
    ConflictError,           # 409
    UnprocessableError,      # 422
    RateLimitError,          # 429
)

try:
    client.mandates.get("nonexistent-id")
except NotFoundError as e:
    print(f"Not found: {e}")
except AgledgerError as e:
    print(f"API error: {e}")

All API errors include doc_url and suggestion attributes when available.

Known issues

Resources:


Environment variables

Both SDKs read AGLEDGER_API_KEY from the environment if no api_key is passed explicitly. Set AGLEDGER_API_URL (TypeScript) or pass base_url (Python) to point to your instance.