payee
Docs
Payee Developer Documentation

Payee developer documentation

Everything you need to build programmable, borderless stablecoin finance with Circle USDC — for humans, AI agents, and the autonomous systems connecting them.

System Architecture Overview

Payee bridges web3 stablecoin networks, biometric hardware security modules, and autonomous software workflows. Circle developer-controlled USDC wallets are mapped to biometric passkey credentials, giving users high security with a frictionless interface. AI agents interact via scoped Bearer API keys with enforced spending policies.

Integration Flow

Client Layer

Humans (Passkey / WebAuthn)
Agents (Bearer API Key)
Circle CLI Agents (monitored)

Security Gate

Supabase Row-Level Security
Concurrency Key Lock (FOR UPDATE)
spent_today Budget Check

Ledger Layer

Circle Developer-Controlled Wallets
Arc Testnet / Base (USDC)
Arc L1 (On-chain ERC-721 identity)
Decoupled Key Safety: Private keys are never stored in plain text. Humans authorize via hardware biometrics; agent API keys are verified with SHA-256 server-side hashing and never returned after creation.

Claiming a Paytag

Every claimed identifier is minted as a prefix-free on-chain ERC-721 NFT on the Arc Network. Choose whether a handle serves a human user or an autonomous AI agent — each gets a different auth model and metadata standard.

For Humans

Passkey Secured

Personal handles are tied to browser WebAuthn biometrics (Touch ID, Face ID, or hardware keys). No seed phrases or private keys to manage.

1
Visit the public Paytags Identity Portal.
2
Search your desired handle (e.g. myname). Displayed as the branded USDC paytag @myname.
3
Click "Claim Handle with Passkey". Your browser authenticates via biometrics and a gasless Turnkey wallet is created on-chain.

For AI Agents

ERC-8004 Standard

Agent handles represent autonomous on-chain identities. They are configured with spending caps, TEE attestation, and publish discoverable ERC-8004 AgentCard metadata. Agents can also be externally-provisioned Circle CLI wallets linked to Payee for monitoring.

1
Claim a standard human handle first, then visit /onboarding/agent from your dashboard.
2
Configure agent skills (e.g. Trading, Coding), model hash, TEE attestation URL, and daily spending caps.
3
Confirm via biometrics. Payee provisions a secret key (payee_agent_live_…) and publishes the ERC-8004 AgentCard.

Comparative Feature Matrix

FeatureHuman HandleAI Agent Handle
Auth MethodDevice Biometrics (Passkey WebAuthn)Bearer API Key (payee_agent_live_…)
Wallet TypeCircle Developer-Controlled (Payee-provisioned)Circle Agent Wallet (CLI or Payee-provisioned)
Spending CapsUnlimited (owner full custody)Daily limit & per-tx cap enforced server-side
InterfaceConversational Dashboard + WhatsAppJSON REST API / monitored via Agent Control Plane
Metadata SpecStandard ERC-721ERC-8004 AgentCard (discoverable)
Paytag Handleprofiles.usernameprofiles.username OR agent_wallets.agent_paytag

Paytag Registry & Tiers

Paytags establish an industry-standard, unauthenticated public namespace mapping human-readable handles directly on-chain. Registered as dynamic ERC-721 NFT contracts on the Arc Network Layer-2, every Paytag is prefix-free and bridges biometric passkeys for humans with secure hardware enclaves for AI agents.

1 — Identity Classification & Registry Tiers

Payee registry tiers classify every claimed handle into a distinct digital role, mapping them to standard smart-contract rules and metadata standards.

Human Profile

HUMAN

A consumer profile linked to WebAuthn browser biometrics (Touch ID / Face ID). Claiming a personal paytag automatically provisions a seedless, gasless smart wallet to settle USDC directly.

AI Agent Enclave

AGENT

An attestation-backed sovereign identity bound to Trusted Execution Environments (TEEs) and AI model hashes. Runs spending actions programmatically up to strict daily USDC budget caps.

Autonomous Service

SERVICE

A decentralized machine API endpoint. Enables direct web3 billing paths for LLM token computation pipelines, vector oracle data queries, and secure backend webhook routing.

Team Organization

ORG

A corporate multi-sig treasury or joint enclave handle. Direct corporate invoice settlement, team workspace spend tracking, and automated global client billing.

2 — AI Agent Identity Benefits (ERC-8004)

Upgrading a handle into a verified Agent profile binds dynamic hardware attestation and discovery standards directly to the on-chain identity.

Sovereign Trust & TEE Attestations

Every Agent Paytag binds its identifier to hardware-level Intel SGX, AMD SEV, or AWS Nitro Enclave attestation documents and LLM hashes on-chain. Third-party nodes verify the agent runs unmodified source logic before transacting.

Standardized ERC-8004 Discovery

Registered agents publish dynamic discovery URIs on-chain pointing to standard AgentCard JSONs. A2A clients, discoverability indexers, and MCP servers look up communication endpoints and capabilities automatically.

Bounded Budget Authority

Eliminate vulnerable Master private keys from script variables. Agents use restricted, budget-bounded delegated keys that manage up to set daily thresholds, fully revocable by the human owner with one click.

Inter-Agent Reputation (A2A)

As agents collaborate in complex loops, prefix-free USDC paytags enable clean audit-friendly payment logs. Agents score counterparties based on success rates, establishing an on-chain reputation matrix.

3 — Registry Technical Specifications & FAQs

General protocol architecture questions regarding namespace guidelines, network relays, and gas sponsorship.

Q: Are Paytags locked to a specific prefix like @ or $?

No. The Paytags namespace is completely prefix-free inside the PayeeRegistry contract on-chain. You claim and resolve direct alphanumeric strings (letters, numbers, and underscores). The @ character is strictly a UI/UX branding convention.

Q: How does on-chain resolution occur?

Payee uses the Arc Network Layer-2 L1 registry contract for live EVM broadcasts. The contract binds the handle string to the Circle USDC EVM wallet address, fully searchable by any web3 RPC client globally.

Q: Is there any gas or cost associated with claiming?

No. Payee runs a fully gasless relayer infrastructure. All minting, updating, and registry transfers are sponsored behind the scenes by the protocol, giving users a completely gas-free onboarding experience.

Developer Use Cases

Payee provides simple programmatic tools so developers can integrate stablecoin transactions into multiple workflows — serving both people and autonomous backend processes.

Conversational Money

Enable biometric sign-in and conversational AI chat. Humans send money with plain English: "send 2 USDC to @sarah" — no cryptographic complexity required.

Learn more

Autonomous AI Agents

Equip scripts or LLM agents with scoped Bearer API keys. Set daily budgets and per-tx caps. The server enforces limits server-side before any transfer is submitted to Circle.

Learn more

Seedless Smart Accounts

Turnkey HSM passkeys eliminate seed phrases. Biometrics authorize every transaction — private keys are generated in hardware and never leave the enclave.

Learn more

Agent Control Plane

Link Circle Agent Wallets (provisioned on your PC via CLI) to the Payee dashboard. Monitor per-agent spending, pause agents, set budgets, and review transaction logs in real time.

Learn more

ERC-8004 Discovery

Every agent handle auto-publishes a verified AgentCard at /api/agent/{username}/agent-card.json. Discovery indexers, A2A clients, and MCP servers can find and verify agent capabilities.

Learn more

WhatsApp Payments

Send and receive USDC via WhatsApp messages. The Payee conversational layer understands payment intents and routes them through the same budget-gated API.

Learn more

Agent Control Plane

The Agent Control Plane turns Payee into a financial oversight dashboard for AI agents running on your PC. Agents are provisioned externally via the Circle CLI, then linked to your Payee account by providing their wallet ID and address. Payee syncs transactions from the Circle API (with an on-chain ethers.js fallback) and stores them locally for fast querying.

How it works

Provision on PC

Run the Circle CLI on your machine to create an Agent Wallet. Copy the wallet ID and on-chain address from the output.

Link to Payee

Paste the wallet ID and address into the Agents dashboard. Assign a unique agent paytag (@my-claude-agent), type, and budget policy.

Monitor & Control

Payee syncs transactions every time you open the dashboard. View per-agent spend, pause an agent with one click, or fund it directly.

Step 1 — Provision on your PC (Circle CLI)

Terminal
# Install the Circle CLI
npm install -g @circle-fin/cli

# Authenticate (OTP sent to your email)
circle wallet login you@example.com

# List your agent wallets — copy "id" and "address"
circle wallet list --chain BASE --output json

Transaction Sync Architecture

Primary: Circle REST API

Queries GET /v1/w3s/transactions?walletIds={id}. Works for wallets provisioned under the same Circle developer account as Payee.

Fallback: On-chain ethers.js indexing

If the Circle API fails (e.g. externally-provisioned wallet), Payee uses provider.getLogs() to index USDC ERC-20 Transfer events directly. Works for any EVM wallet on Base / Arbitrum — no API key needed.

Cache: agent_wallet_transactions

Results are upserted into Postgres using circle_tx_id as the idempotency key — safe to re-run on every sync without creating duplicates.

Global paytag uniqueness: An agent paytag (e.g. @my-claude-agent) is checked for uniqueness across both profiles.username (human handles) and agent_wallets.agent_paytag(linked agent wallets). The paytag resolves to the agent's on-chain address via the standard /api/wallet/resolve-username endpoint, which returns handle_type: "agent" so callers can distinguish agent identities.

Workflows & Spending Rules

Every operation executed through Payee follows strict database locks and safety triggers. Autonomous scripts cannot drain wallets due to software loops, double-spend attempts, or credential theft.

1

Claiming an On-Chain Handle

Users register a unique identifier (e.g. @arbitrage_bot). This handle is anchored via smart contracts to a secure EVM address on the Arc L1 or Base ledger. The Arc registry contract supports agentCardURI() queries for decentralized indexers.

2

Provisioning Delegated API Keys

Instead of sharing biometrics or seed phrases with software bots, users provision scoped API keys starting with payee_agent_live_. The user defines exact limits: e.g. daily_limit = 20 USDC and single_tx_limit = 5 USDC. Keys are stored as SHA-256 hashes and never returned after creation.

3

Concurrency Safety Gate

When the API receives a transfer command, the database locks the api_key row using SELECT ... FOR UPDATE. It verifies the key is active, checks that the UTC midnight reset has fired if needed, confirms both single-tx and daily limits, then submits to Circle's developer wallet API.

4

UTC Midnight Budget Reset

The spent_today counter resets automatically at UTC midnight. The last_spent_reset timestamp is checked on every API call — if a calendar day has passed since the last reset, spent_today is zeroed before the budget check runs.

Quickstart & Integration Code

Create an API key in Dashboard → Settings → Agent Keys. The key is shown only once — copy it immediately. All requests require the key in the Authorization header. The toAddress field accepts both 0x… addresses and @paytag handles.

agent.ts
// Payee Agent SDK — Node.js / TypeScript
const BASE_URL = 'https://stablepayee.xyz'
const API_KEY  = 'payee_agent_live_YOUR_KEY_HERE'

const headers = {
  'Authorization': `Bearer ${API_KEY}`,
  'Content-Type':  'application/json',
}

async function run() {
  // 1 — Fetch wallet balance
  const balRes  = await fetch(`${BASE_URL}/api/wallet/balance`, { headers })
  const { balance, username } = await balRes.json()
  console.log(`💰 @${username} balance: ${balance} USDC`)

  // 2 — Send to a paytag (human or agent)
  const sendRes = await fetch(`${BASE_URL}/api/wallet/send`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ toAddress: '@sarah', amount: '2.00' }),
  })
  const send = await sendRes.json()
  if (sendRes.ok) console.log('✅ Sent! transferId:', send.transferId)
  else            console.error('❌ Failed:', send.error)

  // 3 — Resolve any paytag to its on-chain address
  const resolve = await fetch(
    `${BASE_URL}/api/wallet/resolve-username?username=sarah`
  )
  const { address, handle_type } = await resolve.json()
  console.log(`📍 @sarah → ${address} (type: ${handle_type})`)
}

run()

Wallet API Endpoints

All wallet API requests must include the API key: Authorization: Bearer payee_agent_live_…

GET/api/wallet/balance
Retrieve wallet balance & paytag

Response (200):

{
  "success": true,
  "balance": "250.00",
  "address": "0x5462D682dB237A3a992687Bc628F5b18B33eD0c1",
  "username": "arbitrage_bot",
  "onchainTxHash": "0xabc123..."   // registration tx hash
}
GET/api/wallet/transactions
Transaction history (enriched with agent metadata)

Response (200):

{
  "success": true,
  "transactions": [
    {
      "id": "tx_87f9da52",
      "amounts": ["10.00"],
      "sourceAddress": "0x5462D682dB237A3a992687Bc628F5b18B33eD0c1",
      "destinationAddress": "0x9812A6bC72e92a83C25Dcb654c6A7F2D179c3d4a",
      "type": "OUTBOUND",
      "state": "COMPLETE",
      "createDate": "2026-05-22T19:30:15Z",
      "isAgentTx": true,           // true if sent via an agent API key
      "agentName": "Trading Bot"   // name label of the key used
    }
  ]
}
POST/api/wallet/send
Send USDC — accepts 0x address or @paytag

Request Body:

{
  "toAddress": "0x9812A6bC72e92a83C25Dcb654c6A7F2D179c3d4a",
  // OR paytag (human or agent):
  "toAddress": "@sarah",
  "amount": "1.50"
}

Success (200):

{ "success": true, "transferId": "4c94f58c-4f9e-4e3a-b2c3-92f7e8a9f0d1" }

Error — daily limit exceeded (403):

{ "error": "Amount exceeds daily spending limit. Remaining budget: 0.50 USDC" }
GET/api/wallet/resolve-username
Resolve any paytag → on-chain address

Query params: ?username=sarah — no @ prefix needed

Response (200):

{
  "success": true,
  "address": "0x5462D682dB237A3a992687Bc628F5b18B33eD0c1",
  "handle_type": "human",   // "human" | "agent"
  "username": "sarah"
}

// For a linked Circle Agent Wallet paytag:
{
  "success": true,
  "address": "0xAbCd...1234",
  "handle_type": "agent",
  "username": "my-claude-agent"
}

Resolves across both human paytags (profiles.username) and agent paytags (agent_wallets.agent_paytag). Human paytags take priority on collision.

Agent Wallets API

These endpoints manage the Agent Control Plane — linking, listing, updating, and syncing Circle Agent Wallets. All requests require a valid user session (cookie-based auth, not an agent API key).

GET/api/agent-wallets
List all linked agent wallets with spend stats

Response (200):

{
  "success": true,
  "wallets": [
    {
      "id": "uuid",
      "agent_paytag": "my-claude-agent",
      "display_name": "My Claude Code Agent",
      "agent_type": "coding",
      "circle_address": "0xAbCd...1234",
      "chain": "BASE",
      "daily_limit": 20,
      "per_tx_limit": 5,
      "is_paused": false,
      "last_synced_at": "2026-06-01T04:00:00Z",
      "spent_today": 3.50,         // computed from cached tx log
      "tx_count_today": 2
    }
  ]
}
POST/api/agent-wallets
Link a new Circle Agent Wallet

Request Body:

// Link a Circle Agent Wallet provisioned via CLI
const res = await fetch('https://stablepayee.xyz/api/agent-wallets', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_SESSION_TOKEN',
    'Content-Type':  'application/json',
  },
  body: JSON.stringify({
    circle_wallet_id: 'a7f3d2e1-9b4c-4f8a-b2e5-1c3d5e7f9a0b', // from: circle wallet list
    circle_address:   '0xAbCd...1234',                          // from: circle wallet list
    chain:            'BASE',
    agent_paytag:     'my-claude-agent',
    display_name:     'My Claude Code Agent',
    agent_type:       'coding',
    daily_limit:      20,
    per_tx_limit:     5,
  }),
})
const { wallet } = await res.json()
console.log('Linked agent paytag:', `@${wallet.agent_paytag}`)
PATCH/api/agent-wallets/[id]
Update budget limits, display name, or pause state

Updatable fields:

{
  "display_name": "Upgraded Agent",
  "daily_limit":   50,
  "per_tx_limit":  10,
  "is_paused":     true,   // soft pause (display only)
  "agent_type":    "trading"
}
POST/api/agent-wallets/[id]/sync
Manually trigger transaction sync for one agent

Response (200):

{ "success": true, "synced": 5, "source": "circle" }
// source: "circle" | "onchain" (fallback)
GET/api/agent-wallets/[id]/transactions
Cached transaction log for one agent

Query params: ?limit=50 (max 100)

Response (200):

{
  "success": true,
  "transactions": [
    {
      "id": "uuid",
      "tx_direction": "OUTBOUND",
      "amount_usdc": 2.50,
      "destination_address": "0x...",
      "destination_paytag": "sarah",  // null if unknown
      "state": "COMPLETE",
      "source": "circle",             // "circle" | "onchain"
      "blockchain_tx_hash": "0x...",
      "circle_created_at": "2026-06-01T03:00:00Z"
    }
  ]
}
DELETE/api/agent-wallets/[id]
Unlink an agent wallet (soft delete — history preserved)
{ "success": true, "message": "Agent wallet unlinked" }

ERC-8004 AgentCards & Discovery

Every AI Agent handle on Payee auto-publishes a standards-compliant ERC-8004 AgentCarddocument. Discovery indexers, A2A clients, and MCP servers can fetch and verify an agent's identity, capabilities, and payment endpoint from a single URL.

Domain Discovery Manifest — /.well-known/agent-registration.json

Proves that stablepayee.xyz is the canonical host for the on-chain registry, enabling search engines and indexers to verify authority.

{
  "protocol":    "ERC-8004",
  "domain":      "stablepayee.xyz",
  "registry":    "0xdeDC4abF8788dc0DE36567D92b04da3Fb9d803F7",
  "description": "Canonical on-chain payment identity registry for humans and AI agents."
}

AgentCard JSON — /api/agent/{username}/agent-card.json

CAIP-10 wallet address (eip155:{chainId}:{address}), A2A and PaymentAPI endpoints, skills, and trust mechanisms. Returned with Cache-Control: max-age=3600 and open CORS for agent discovery clients.

{
  "type":        "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
  "name":        "Payment Assistant",
  "description": "@olayemi is a programmable Paytag for AI agents on Payee.",
  "image":       "https://stablepayee.xyz/api/metadata/olayemi",
  "agentWallet": "eip155:2911:0x5462D682dB237A3a992687Bc628F5b18B33eD0c1",
  "endpoints": [
    {
      "name":      "A2A",
      "endpoint":  "https://stablepayee.xyz/api/agent/olayemi/agent-card.json",
      "version":   "0.3.0",
      "a2aSkills": ["payments", "agent-communication"]
    },
    {
      "name":        "PaymentAPI",
      "endpoint":    "https://stablepayee.xyz/api/wallet/send",
      "version":     "1.0",
      "description": "USDC payment endpoint — POST { toAddress, amount }. Accepts paytag @handles.",
      "authentication": {
        "scheme":      "Bearer",
        "description": "Payee Agent API key: payee_agent_live_…"
      }
    },
    {
      "name":        "IdentityMetadata",
      "endpoint":    "https://stablepayee.xyz/api/metadata/olayemi",
      "version":     "1.0",
      "description": "ERC-721 NFT metadata for this Paytag identity token."
    }
  ],
  "capabilities":   ["payments", "agent-communication"],
  "supportedTrust": ["reputation", "crypto-economic"],
  "owner":   "0x7a63750B1B619472e38c7C74C978D7Dfe36e39A3",
  "paytag":  "@olayemi",
  "version": "1.0.0",
  "provider": {
    "name":             "Payee Identity Protocol",
    "url":              "https://stablepayee.xyz",
    "registryContract": "0xdeDC4abF8788dc0DE36567D92b04da3Fb9d803F7",
    "chain":            "eip155:2911"
  }
}
On-Chain Metadata Anchoring: The registry contract (deployed on Arc Testnet at 0xdeDC4abF8788dc0DE36567D92b04da3Fb9d803F7) supports agentCardURI(string) queries directly, enabling decentralized indexers to resolve the AgentCard via the EVM ledger. It also implements supportsInterface() (ERC-165) and burn() to release handles.

Database Schema & Security Policies

Payee uses Supabase Postgres with Row-Level Security on every user-owned table. Agent API keys are stored as SHA-256 hashes and never returned after creation. Concurrency is handled with SELECT … FOR UPDATE row locks.

agent_api_keys

CREATE TABLE IF NOT EXISTS agent_api_keys (
  id                UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  profile_id        UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
  key_hash          TEXT UNIQUE NOT NULL,      -- SHA-256 of the raw key
  name              TEXT NOT NULL,             -- user-defined label
  key_prefix        TEXT NOT NULL,             -- first 12 chars for display
  daily_limit       NUMERIC(18, 6) NOT NULL DEFAULT 10.000000,
  single_tx_limit   NUMERIC(18, 6) NOT NULL DEFAULT 5.000000,
  spent_today       NUMERIC(18, 6) NOT NULL DEFAULT 0.000000,
  last_spent_reset  TIMESTAMPTZ    NOT NULL DEFAULT NOW(),
  is_active         BOOLEAN        NOT NULL DEFAULT TRUE,
  created_at        TIMESTAMPTZ    NOT NULL DEFAULT NOW(),
  last_used_at      TIMESTAMPTZ
);

ALTER TABLE agent_api_keys ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_agent_keys" ON agent_api_keys
  FOR ALL USING (auth.uid() = profile_id);

agent_wallets — Circle CLI linked wallets

CREATE TABLE IF NOT EXISTS agent_wallets (
  id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  profile_id          UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,

  -- Circle Agent Wallet (from: circle wallet list --output json)
  circle_wallet_id    TEXT NOT NULL,       -- Circle internal UUID
  circle_address      TEXT NOT NULL,       -- 0x... on-chain address
  chain               TEXT NOT NULL DEFAULT 'BASE',

  -- Identity (globally unique across profiles.username AND here)
  agent_paytag        TEXT UNIQUE NOT NULL, -- e.g. "my-claude-agent"
  display_name        TEXT,
  agent_type          TEXT DEFAULT 'coding', -- coding | trading | service | custom

  -- Budget policy (display + alerting; enforcement is CLI-side)
  daily_limit         NUMERIC(18, 6),
  per_tx_limit        NUMERIC(18, 6),

  -- State
  is_active           BOOLEAN NOT NULL DEFAULT TRUE,
  is_paused           BOOLEAN NOT NULL DEFAULT FALSE,
  last_synced_at      TIMESTAMPTZ,
  created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

ALTER TABLE agent_wallets ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_agent_wallets" ON agent_wallets
  FOR ALL USING (auth.uid() = profile_id);

agent_wallet_transactions — cached tx log

CREATE TABLE IF NOT EXISTS agent_wallet_transactions (
  id                   UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  agent_wallet_id      UUID NOT NULL REFERENCES agent_wallets(id) ON DELETE CASCADE,
  profile_id           UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,

  -- Idempotency key: txHash-logIndex (onchain) or Circle tx UUID
  circle_tx_id         TEXT UNIQUE NOT NULL,
  source               TEXT NOT NULL DEFAULT 'circle', -- 'circle' | 'onchain'

  tx_direction         TEXT NOT NULL, -- 'INBOUND' | 'OUTBOUND'
  amount_usdc          NUMERIC(18, 6) NOT NULL,
  destination_address  TEXT,
  source_address       TEXT,
  state                TEXT NOT NULL DEFAULT 'COMPLETE',
  blockchain_tx_hash   TEXT,

  -- Enrichment (resolved after sync)
  destination_paytag   TEXT,    -- resolved @paytag if destination is a Payee user
  service_label        TEXT,    -- human-readable label from Circle metadata

  circle_created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  synced_at            TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_awt_agent  ON agent_wallet_transactions(agent_wallet_id);
CREATE INDEX idx_awt_date   ON agent_wallet_transactions(circle_created_at DESC);

ALTER TABLE agent_wallet_transactions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_agent_txs" ON agent_wallet_transactions
  FOR ALL USING (auth.uid() = profile_id);