Documentation

AgentPact is a spending guardrail for AI agents. The owner of an agent writes a Pact; the agent asks AgentPact before every financial action; AgentPact answers allow, deny, or approval required, commits the budget, and records the decision. The policy is enforced server-side; the MCP server and SDK are clients.

Quickstart (no signup)

Create a throwaway sandbox agent with a Research Pact and a one-hour session key:

curl -X POST https://api.agentpact.dev/api/v1/demo/session

Ask whether the agent may pay 0.5 USDG to a research API:

curl -X POST https://api.agentpact.dev/api/v1/authorize \
  -H "Authorization: Bearer pact_sk_..." \
  -H "content-type: application/json" \
  -d '{"action":"payment","amount":"0.5","asset":"USDG","destination":"0xResearchApi"}'
{
  "decisionId": "pact_dec_4a8qn2tqye",
  "decision": "approval_required",
  "reasons": ["new_destination"],
  "checks": {
    "agent_active": "pass", "action_allowed": "pass", "asset_allowed": "pass",
    "destination_not_blocked": "pass", "destination_known": "ask",
    "transaction_cap": "pass", "daily_budget": "skip", "session_budget": "pass", "velocity": "skip"
  },
  "remainingDailyBudget": "25",
  "pactId": "pact_p4hiratbzfn8", "pactVersion": 1,
  "approvalId": "apr_cn657y8mn4", "approvalExpiresAt": "2026-09-12T07:00:41.946Z"
}

The Research Pact asks the owner the first time an agent pays a new destination. Try a swap (denied by the Pact), an amount above 1 USDG (asks), or a run of small payments (allowed until the 25 USDG day is spent). Then read GET /budget and GET /activity with the same key. Or use the live demo on the home page.

How a decision is made

Rules run in a fixed order. AI never participates; the limits are deterministic.

  1. Agent frozen → deny
  2. Action type set to deny → deny
  3. Asset not in allowed_assetsdeny
  4. Destination in blocked_destinationsdeny
  5. Destination unknown and unknown_contracts is deny → deny
  6. Action type set to ask → approval required
  7. Amount above transaction_limit (or at/above approval_threshold) → approval required
  8. Destination unknown and unknown_contracts is ask → approval required
  9. Daily budget, session budget, or hourly velocity would be exceeded → deny
  10. Otherwise → allow, and the amount is committed against the budget

A destination is unknown when it is not in allowed_destinations and this agent has never been allowed to pay it before. This is computed by the server; an agent cannot claim a destination is known.

The budget step is one atomic database write: the allow row is only inserted if, at that instant, committed spend plus this amount still fits. Concurrent requests cannot overspend. The daily window is rolling 24 hours. An approved approval counts against the budget from the moment the owner approves it, and approving never lets the daily limit be exceeded; if the day is already spent, the approval is recorded as denied.

MCP

Hosted server: https://mcp.agentpact.dev/mcp (Streamable HTTP; /sse for legacy clients). The agent's session key travels with the connection, never as a tool argument, so it stays out of the model's context.

Claude Code / Claude Desktop / Codex (any client that supports headers):

{
  "mcpServers": {
    "agentpact": {
      "type": "http",
      "url": "https://mcp.agentpact.dev/mcp",
      "headers": { "Authorization": "Bearer pact_sk_..." }
    }
  }
}

Clients without header support can put the key in the URL:

https://mcp.agentpact.dev/mcp?key=pact_sk_...

Tools:

ToolWhat it does
pact_statusAgent status, active Pact summary, today's budget
pact_budgetDaily limit, spent, remaining, per-transaction cap
pact_authorizeaction, amount, asset, destination, memo → allow | deny | approval_required with checks
pact_approval_statusapprovalId → pending | approved | denied | expired
pact_activityRecent decisions, newest first
pact_payAuthorize AND execute a payment from the agent's managed wallet; returns txHash on allow
pact_walletManaged wallet address and balances, for funding checks
pact_swapAuthorize a swap. Swap execution not deployed yet; authorizes only

Suggested system prompt line for the agent: “Before any payment, swap, transfer, or contract call, call pact_authorize. Only proceed on allow. On approval_required, poll pact_approval_status. On deny, do not retry the same request.”

TypeScript SDK

npm install @agentpact-dev/sdk
import { AgentPact } from "@agentpact-dev/sdk";

// Sandbox, no signup:
const { client } = await AgentPact.sandbox();

// Real agent, key issued by the owner:
const pact = new AgentPact({ sessionKey: process.env.AGENT_SESSION_KEY! });

const result = await pact.authorize({
  action: "payment", amount: "2", asset: "USDG", destination: "0x…", memo: "NVDA filings",
});

if (result.decision === "allow") {
  // proceed
} else if (result.decision === "approval_required") {
  const approval = await pact.waitForApproval(result.approvalId!);
  if (approval.status === "approved") { /* proceed */ }
} else {
  // denied: result.reasons
}

await pact.budget();
await pact.activity({ limit: 20 });

Owners: agents, Pacts, session keys

Owners sign in with their wallet (no email, no password): the API returns a nonce message, the wallet signs it, and the API returns a token. The dashboard does this with any injected wallet. From the REST API:

# 1. nonce + message to sign
curl "https://api.agentpact.dev/api/v1/auth/nonce?address=0xYourWallet"
# 2. sign message with the wallet, then
curl -X POST https://api.agentpact.dev/api/v1/auth/verify \
  -H "content-type: application/json" \
  -d '{"message":"<message>","signature":"0x..."}'
# → { "token": "...", "owner": { ... } }

Create an agent from a template (research, trading, worker) or a full Pact config, then issue a session key:

curl -X POST https://api.agentpact.dev/api/v1/agents \
  -H "Authorization: Bearer <owner token>" -H "content-type: application/json" \
  -d '{"name":"Research Agent","template":"research"}'

curl -X POST https://api.agentpact.dev/api/v1/agents/agt_.../session \
  -H "Authorization: Bearer <owner token>" -H "content-type: application/json" \
  -d '{"label":"laptop","expiresInHours":168}'
# → { "sessionKey": "pact_sk_..." }   shown once, only a hash is stored

Give the key to the agent. It identifies the agent on every call. An agent cannot read or change its Pact beyond what the owner set, cannot approve itself, and cannot extend its own key. Freezing an agent revokes every key at once.

Pact reference

FieldMeaning
nameDisplay name
transaction_limitAutonomous per-transaction cap (decimal string). Above it → approval required
daily_limitHard ceiling on spend per rolling 24h. Exceeding it → deny, never ask
session_limitOptional hard ceiling on lifetime spend of one session key
approval_thresholdOptional: ask at/above this amount even when under transaction_limit
max_actions_per_hourOptional velocity guard on allowed actions
allowed_assetsAsset symbols the agent may use (case-insensitive)
allowed_destinationsTrusted addresses / identifiers. Never treated as unknown
blocked_destinationsAlways denied
actionspayment, swap, transfer, contract_call → allow | ask | deny
unknown_contractsWhat to do with a destination that is neither trusted nor previously allowed: allow | ask | deny
approval_ttl_minutesHow long an approval stays open (default 60)

All amounts are decimal strings with up to 6 decimals. Floats are rejected.

{
  "name": "Research Agent Pact",
  "transaction_limit": "1",
  "daily_limit": "25",
  "allowed_assets": ["USDG"],
  "allowed_destinations": ["0xResearchApi"],
  "actions": { "payment": "allow", "swap": "deny", "transfer": "deny", "contract_call": "ask" },
  "unknown_contracts": "ask",
  "max_actions_per_hour": 120
}

REST reference

Base URL https://api.agentpact.dev/api/v1. Auth: Authorization: Bearer with an owner token (O) or an agent session key (A).

MethodPathAuthPurpose
POST/demo/sessionnoneThrowaway sandbox agent + one-hour key
GET/auth/nonce?address=noneSign-in nonce and message
POST/auth/verifynoneSigned message → owner token
GET/meOCurrent owner
GET/templatesnonePact templates
POST/agentsOCreate agent (+ Pact v1)
GET/agentsOList agents with budgets
GET/agents/:idO AAgent, active Pact, budget
POST/agents/:id/freezeOFreeze + revoke all keys
POST/agents/:id/unfreezeOUnfreeze (keys stay revoked)
GET/agents/:id/pactsO APact version history
POST/agents/:id/pactsONew Pact version (full config)
GET/pacts/:idO AOne Pact version
PATCH/pacts/:idOPartial edit → new version
POST/agents/:id/sessionOIssue session key (shown once)
GET/agents/:id/sessionsOList keys
POST/agents/:id/sessions/:keyId/revokeORevoke one key
POST/authorizeAThe decision (no execution)
POST/payADecision + on-chain execution from the managed wallet
GET/walletAOwn managed wallet address and balances
POST/agents/:id/walletOCreate the agent's managed wallet
GET/agents/:id/walletO AWallet address and balances
GET/budgetAOwn budget
GET/budget/:agentIdO ABudget
GET/activityO ADecisions (?agent= ?decision= ?limit= ?before=)
GET/activity/:idO AOne decision
GET/approvalsO AApprovals (?status=pending)
GET/approvals/:idO AOne approval
POST/approvals/:id/approveOApprove once (budget-checked)
POST/approvals/:id/denyODeny

What is live

CapabilityStatus
Policy engine: actions, assets, destinations, per-tx cap, daily / session / velocity limitsLive
Atomic budget commit, rolling 24h windowLive
Owner wallet sign-in, agents, versioned Pacts, session keys, freezeLive
Approvals: approve once, deny, expiry, budget-checked approvalLive
Audit trail with rule checks per decisionLive
Hosted MCP server, TypeScript SDK, REST APILive
Dashboard (agents, Pact editor, approvals, activity, keys)Live, injected wallet sign-in
Webhooks, live WebSocket feedPlanned
Managed agent wallets (Privy): /pay executes on Robinhood Chain, denies cannot be signedLive
Swap execution; Pact Account contract (non-custodial enforcement)Planned
Transaction simulation, intent-aware riskPlanned