Agent Payment Service: How AI Agents Send and Receive Money on TRON
Every USDT transfer on TRON requires energy. Every energy purchase requires a separate API call to a provider. Every provider has different pricing, different APIs, different availability windows. An AI agent trying to send $5 USDT faces a sequence of decisions that has nothing to do with its actual task.
MERX Agent Payment Service eliminates this entirely. The agent calls send(). MERX handles everything else -- energy estimation, provider selection, resource delegation, transaction broadcast, and confirmation tracking. The agent never sees energy, never checks bandwidth, never interacts with a provider.
This article covers the full technical implementation: how send works under the hood, how receive detects payments in under 3 seconds, how watches persist across sessions, and how billing works with the free tier.
Architecture: Three Services, One Pipeline
The Agent Payment Service runs as three Docker containers on the MERX infrastructure:
merx-zmq-listener -- connects to TRON node ZeroMQ, streams ALL TRC20 Transfer events
merx-agent-payments -- REST API + Redis subscriber + webhook dispatcher + billing
merx-x402-facilitator -- x402 protocol: verify payment, issue JWT authorization
The ZMQ listener is the foundation. It subscribes to two ZeroMQ topics on the MERX TRON Lite FullNode:
contractEventTrigger-- fires when a transfer is included in a block (~3 seconds)solidityEventTrigger-- fires when the block becomes irreversible (~54 seconds, 18 confirmations)
Every TRC20 Transfer event on the entire TRON network passes through this listener. It publishes each event to Redis pub/sub channels. The agent-payments service subscribes to the global channel and matches incoming events against active watches, pending receives, and open invoices.
send() -- Smart Resource Provisioning
When an agent calls POST /api/v1/agent/send, the following happens in sequence:
1. Agent signs USDT transfer TX locally (private key never leaves the agent)
2. Agent sends pre-signed TX to MERX
3. MERX calls triggerconstantcontract to estimate energy needed
4. MERX calls getaccountresource to check what the agent already has
5. Delta = needed - available
6. If delta > 0: MERX purchases energy from cheapest provider, delegates to agent
7. Same check for bandwidth
8. MERX broadcasts the pre-signed TX
9. ZMQ confirms broadcast, agent receives receipt
Step 3 and 4 are parallel requests to the MERX TRON node -- each takes under 2ms. The entire flow from API call to broadcast is typically under 500ms when the agent has sufficient resources, or under 5 seconds when energy must be purchased.
The key insight: most agents making regular transfers already have delegated energy from previous operations. Steps 5-6 are skipped entirely in those cases. The resource check adds ~4ms of overhead for a potential savings of 13 TRX ($3.90) per transaction.
Batch Send
For payroll, distributions, or mass transfers, POST /api/v1/agent/batch-send accepts up to 50 transfers in a single call. Each transfer is a separate signed transaction -- MERX handles resource provisioning and broadcasting for all of them. Failed transfers do not block successful ones. The response includes per-transfer status.
receive() -- Sub-3-Second Detection
POST /api/v1/agent/receive creates a one-time watch on the agent's own address. When a matching TRC20 transfer arrives:
TRON block produced t = 0ms
ZMQ contractEventTrigger fires t = ~3,000ms
Redis pub/sub delivery t = ~3,001ms
Matcher checks amount + token t = ~3,002ms
Webhook dispatched to agent t = ~3,050ms
Total time from on-chain confirmation to webhook: approximately 3 seconds. Compare this to TronGrid polling at 5-10 second intervals -- MERX detection is 2-3x faster and event-driven rather than poll-based.
The receive also fires a second webhook approximately 54 seconds later when solidityEventTrigger confirms the transaction is irreversible. For amounts under $100, the first confirmation is sufficient. For large amounts, agents can wait for the second.
Token Filtering
Both receive and watch support optional token_contract filtering. Pass a specific TRC20 contract address to match only that token, or omit it to match any TRC20 transfer. Event types provide shorthand: usdt_incoming, usdc_incoming, usdd_incoming, or trc20_incoming for any token.
watch() -- Persistent 24/7 Monitoring
Unlike receive (which is one-time), watch creates a persistent subscription that fires on every matching transfer for its entire lifetime -- up to 30 days.
POST /api/v1/agent/watch
{
"address": "TAnyTronAddress",
"event_types": ["trc20_incoming"],
"token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"min_amount": "100.00",
"webhook_url": "https://your-agent.com/events",
"ttl_hours": 720
}
This watch runs 24/7 for 30 days, independent of any conversation or session. The agent does not need to be online. Every USDT transfer over $100 to the watched address triggers a webhook.
Hot Address Protection
If multiple agents watch the same address, the ZMQ listener publishes one event. The matcher fans it out to all matching watches. One blockchain event becomes N webhook calls. This prevents thundering herd problems on popular addresses.
invoice() -- Payment Requests
Invoices are metadata records pointing to the agent's own address. MERX does not create separate deposit addresses -- payments go directly to the agent.
POST /api/v1/agent/invoice/create
{ "amount_usdt": "25.00", "description": "Analysis of 50 documents" }
Response:
{
"invoice_id": "inv_abc123",
"payment_url": "https://merx.exchange/pay/inv_abc123",
"pay_to_address": "TAgentOwnAddress",
"amount_usdt": "25.00"
}
When payment arrives (detected via ZMQ), the invoice status updates to paid and the creator's webhook fires. The payment URL works for both agents and humans.
Billing: Free Tier + Prepaid Balance
Every API key starts with a free tier:
| Operation | Free per month |
|---|---|
| receive() | 10 detections |
| send() | 20 broadcasts |
| swap() | 5 executions |
| watch() | 3 active simultaneously |
| x402 | 50 settlements |
Beyond the free tier, operations are charged from a prepaid USDT balance. Every charge creates a paired entry in the agent billing ledger -- the same double-entry accounting pattern used throughout MERX.
If the balance is insufficient, the API returns HTTP 402 with the current balance and required amount. The agent can top up by transferring USDT to their deposit address.
Global Webhooks
Instead of specifying a webhook URL on each receive/watch, agents can register a global webhook that fires for all events:
POST /api/v1/agent/webhooks
{
"url": "https://your-agent.com/merx-events",
"events": ["payment.detected", "payment.confirmed", "transfer.detected", "schedule.executed"]
}
Webhooks include HMAC signatures for verification. Failed deliveries retry with exponential backoff (immediate, 30s, 5m, 30m, 2h). After 5 failures, the webhook is deactivated.
Scheduled Payments
POST /api/v1/agent/schedule creates one-time or recurring transfers:
// One-time: send $100 on May 1st
{ "to": "TRecipient", "amount": "100.00", "at": "2026-05-01T09:00:00Z" }
// Recurring: every hour
{ "to": "TRecipient", "amount": "1.00", "cron": "0 * * * *", "max_runs": 24 }
The schedule executor runs every 30 seconds, checking for due payments. Execution results are delivered via global webhooks.
Non-Custodial Design
MERX never holds private keys. The agent owns its wallet. MERX provides three services:
- Energy routing for send() -- purchase resources from the cheapest provider
- Event detection for receive() and watch() -- stream events from the TRON node
- Payment verification for x402 -- verify on-chain and issue JWT
The agent registers its TRON address once. All subsequent operations reference this address. Balance tracking is on-chain -- MERX does not maintain a shadow balance for the agent's wallet.
SDK Integration
import { MerxClient } from 'merx-sdk'
const merx = new MerxClient({ apiKey: process.env.MERX_API_KEY })
// Register
await merx.agent.register('TYourAddress')
// Send with automatic resource provisioning
await merx.agent.send({ to: 'TRecipient', amount_usdt: '5.00', signed_tx: '...' })
// Receive with webhook
await merx.agent.receive({ amount: '5.00', webhookUrl: 'https://...' })
// Watch any address for any TRC20
await merx.agent.watch({
address: 'TAnyAddress',
eventTypes: ['trc20_incoming'],
webhookUrl: 'https://...',
ttlHours: 720
})
// Swap
await merx.swap.execute({ from: 'USDC', to: 'USDT', amount: '5.00' })
// Schedule
await merx.agent.schedule({ to: 'TRecipient', amount: '100.00', at: '2026-05-01T09:00:00Z' })
The Python SDK provides the same interface:
from merx import MerxClient
merx = MerxClient(api_key="sk_live_...")
merx.agent.register("TYourAddress")
merx.agent.receive(amount="5.00", webhook_url="https://...")
What This Enables
With the Agent Payment Service, an AI agent on TRON has the same financial primitives as a Stripe-integrated web application:
- Send any TRC20 token without understanding energy
- Receive with sub-3-second detection
- Watch addresses 24/7 without being online
- Invoice other agents or humans
- Schedule recurring payments
- Batch up to 50 transfers in one call
- Swap between tokens via SunSwap V2
- Pay for APIs via x402 protocol
The infrastructure runs on a dedicated TRON node with direct ZeroMQ access -- no TronGrid dependency, no polling, no rate limits beyond the subscription tier.
Agent Payment Service is live at agent.merx.exchange. x402 Facilitator is live at x402.merx.exchange. Both are operational on TRON mainnet.