Payouts API

Send crypto from your workspace wallet to any address on Base. Supports USDC, ETH, and ADAO. Payouts are synchronous — the transaction hash is returned immediately in the response.

How Payouts Work
Send funds directly from your workspace wallet
Your Workspace Wallet          PayDirect              Recipient
        │                           │                      │
   1.   │── POST /api/v1/payouts ──▶│                      │
        │   tokenSymbol: "USDC"     │                      │
        │   amount: "50.00"         │                      │
        │   destinationAddress      │                      │
        │                           │                      │
   2.   │                           │── Check balance      │
        │                           │── Send on-chain ────▶│
        │                           │                      │
   3.   │◀── { txHash, status:      │                      │
        │      "completed" }        │                      │
        │                           │                      │
   4.   │                           │── Webhook ──▶ payout.completed
        │                           │   (secondary confirmation)

Synchronous

Transaction hash returned immediately. No polling needed.

Auto Gas Funding

PayDirect auto-funds workspace wallets with ETH for gas. If your wallet runs low, the system tops it up automatically.

Idempotent

Use idempotencyKey to prevent duplicate payouts on retry.

POST
/api/v1/payouts
Send a payout from your workspace wallet

Request Body

FieldTypeRequiredDescription
tokenSymbolstringYes"USDC", "ETH", or "ADAO"
amountstringYesPositive amount (e.g. "50.00")
destinationAddressstringYesRecipient Base address (0x..., 42 chars)
descriptionstringNoHuman-readable description (e.g. "Affiliate commission Q1")
metadataobjectNoArbitrary key-value metadata
idempotencyKeystringNoUnique key to prevent duplicate payouts on retry
walletTypestringNo"eoa" or "smart_wallet". Defaults to workspace setting. Smart wallet payouts are gasless.

Headers

HeaderRequiredDescription
AuthorizationYesBearer pd_test_... or pd_live_...

cURL Example

curl -X POST https://www.paydirect.com/api/v1/payouts \
  -H "Authorization: Bearer pd_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "tokenSymbol": "USDC",
    "amount": "50.00",
    "destinationAddress": "0xRecipientAddress...",
    "description": "Affiliate commission payout",
    "metadata": { "partnerId": "partner_123", "period": "2026-Q1" },
    "idempotencyKey": "payout-partner_123-2026Q1"
  }'

TypeScript SDK

const { payout, timestamp } = await client.createPayout({
  tokenSymbol: "USDC",
  amount: "50.00",
  destinationAddress: "0xRecipientAddress...",
  description: "Affiliate commission payout",
  metadata: { partnerId: "partner_123" },
  idempotencyKey: "payout-partner_123-2026Q1",
});

console.log(`Sent! TX: ${payout.txHash}`);
// payout.status is always "completed" on success

Response (200 OK)

{
  "payout": {
    "txHash": "0x1a2b3c4d5e6f...",
    "amount": "50.00",
    "tokenSymbol": "USDC",
    "destinationAddress": "0xRecipientAddress...",
    "fromAddress": "0xYourWorkspaceWallet...",
    "environment": "live",
    "status": "completed",
    "description": "Affiliate commission payout",
    "metadata": { "partnerId": "partner_123", "period": "2026-Q1" }
  },
  "timestamp": "2026-02-16T14:36:22.000Z"
}

Synchronous execution: Unlike payments (which go through a lifecycle), payouts execute immediately. The txHash and status: "completed" are in the response. No need to poll or wait for a webhook.

Balance check: PayDirect checks your workspace wallet balance before sending. If insufficient, you'll get a 400 with available and requested amounts.

Error Responses
Common payout errors

Insufficient Balance (400)

{
  "error": "Insufficient balance",
  "available": "12.50",
  "requested": "50.00",
  "tokenSymbol": "USDC"
}

Missing Fields (400)

{
  "error": "Missing required fields: tokenSymbol, amount, destinationAddress"
}

Invalid Address (400)

{
  "error": "Invalid destinationAddress format"
}

Insufficient Gas / ETH (400)

{
  "error": "Insufficient ETH for gas",
  "ethBalance": "0",
  "required": "~0.001 ETH",
  "action": "PayDirect auto-funds gas periodically. Retry in a few minutes, or send ETH to your workspace wallet."
}

ERC-20 payouts (USDC, ADAO) require a small ETH balance for gas. PayDirect auto-funds workspace wallets with ETH periodically. If you see this error, the gas cron may not have run yet — retry shortly.

No Workspace Wallet (400)

{
  "error": "No workspace wallet found. Provision a wallet first."
}

Payout Failed (500)

{
  "error": "Payout failed",
  "code": "PAYOUT_FAILED",
  "detail": "Underlying error message",
  "failStreak": 1,
  "remainingBeforePause": 2,
  "circuitBreaker": false
}

On failure, a payout.failed webhook event is also fired with the error details including failStreak count.

Circuit Breaker — Workspace Paused (503)

{
  "error": "Workspace payouts paused: 3 consecutive payout failures: ...",
  "code": "CIRCUIT_BREAKER_OPEN",
  "detail": "Underlying error message",
  "failStreak": 3,
  "remainingBeforePause": 0,
  "circuitBreaker": true
}

After 3 consecutive payout failures, the workspace is automatically paused to prevent runaway losses. All subsequent payout requests return 503 until the workspace is resumed. The workspace auto-resumes after 30 minutes, or you can resume manually.

Idempotency
Prevent duplicate payouts on retry

Include an idempotencyKey in the request body to ensure the same payout is not sent twice. If a payout with the same key was already sent, PayDirect returns the original result instead of sending again.

Duplicate Response (200 OK)

{
  "payout": {
    "txHash": "0x1a2b3c4d5e6f...",
    "status": "completed",
    "note": "Duplicate request -- returning previous result"
  }
}
Recommended key patterns:
  • payout-partner_123-2026Q1 — partner commission
  • affiliate-aff_456-jan2026 — affiliate payout
  • adao-cashout-user_789-1708100000 — user ADAO cashout
GET
/api/v1/payouts
List payout history for the workspace

Query Parameters

ParameterTypeDescription
limitnumberMax results (1-100, default: 50)
offsetnumberPagination offset (default: 0)

cURL Example

curl "https://www.paydirect.com/api/v1/payouts?limit=20&offset=0" \
  -H "Authorization: Bearer pd_live_abc123..."

Response (200 OK)

{
  "payouts": [
    {
      "txHash": "0x1a2b3c4d5e6f...",
      "type": "ERC20",
      "details": {
        "amount": "50.00",
        "tokenSymbol": "USDC",
        "destinationAddress": "0xRecipient...",
        "description": "Affiliate commission payout"
      },
      "createdAt": "2026-02-16T14:36:22.000Z"
    },
    {
      "txHash": "0x7a8b9c0d1e2f...",
      "type": "ETH",
      "details": {
        "amount": "0.05",
        "tokenSymbol": "ETH",
        "destinationAddress": "0xOtherRecipient..."
      },
      "createdAt": "2026-02-15T10:20:00.000Z"
    }
  ],
  "total": 42,
  "limit": 20,
  "offset": 0
}
Payout Webhook Events
Events fired during the payout lifecycle
EventDescription
payout.completed
Payout transaction confirmed on Base. Includes txHash, amount, tokenSymbol, destinationAddress, fromAddress.
payout.failed
Payout failed due to insufficient balance, chain error, or other issue. Includes error message.

Webhook Payload Example

{
  "id": "delivery-uuid-...",
  "event": "payout.completed",
  "data": {
    "txHash": "0x1a2b3c4d5e6f...",
    "amount": "50.00",
    "tokenSymbol": "USDC",
    "destinationAddress": "0xRecipientAddress...",
    "fromAddress": "0xYourWorkspaceWallet...",
    "environment": "live",
    "status": "completed",
    "description": "Affiliate commission payout",
    "metadata": { "partnerId": "partner_123" }
  },
  "timestamp": "2026-02-16T14:36:22.000Z"
}
Note: Since payouts are synchronous, the webhook is a secondary confirmation. You already have the txHash from the POST response. Webhooks are useful for asynchronous processing, audit logging, or systems that can't wait for the synchronous response.
Circuit Breaker
Automatic protection against cascading payout failures

PayDirect includes a circuit breaker that automatically pauses a workspace after consecutive payout failures. This prevents runaway losses from bad configuration, depleted wallets, or chain issues.

Payout #1 fails → failStreak: 1  (warning in response)
Payout #2 fails → failStreak: 2  (warning in response)
Payout #3 fails → failStreak: 3  → WORKSPACE PAUSED
                                   → Admin email sent
                                   → All payouts return 503
                                   → Auto-resume after 30 min
                                     OR manual resume via API

Response Fields

FieldTypeDescription
codestringCIRCUIT_BREAKER_OPEN, INSUFFICIENT_GAS, or PAYOUT_FAILED
failStreaknumberConsecutive failure count for the workspace
remainingBeforePausenumberHow many more failures before the circuit breaker trips (0 if already paused)
circuitBreakerbooleantrue if the workspace is currently paused

Pre-flight Check (503 Before Execution)

When the circuit breaker is open, the API returns 503 immediately without attempting the on-chain transaction:

{
  "error": "Workspace payouts paused: 3 consecutive payout failures: ...",
  "code": "CIRCUIT_BREAKER_OPEN",
  "circuitBreaker": true,
  "failStreak": 3,
  "pausedAt": "2026-02-16T14:00:00.000Z",
  "resumeAction": "Fix the issue, then POST /api/v1/workspaces/{id}/resume or wait 30 minutes for auto-retry"
}

Resuming a Paused Workspace

Three ways to resume:

Auto-Resume

After 30 minutes, the next payout attempt automatically resumes the workspace and retries.

API Resume

POST /api/v1/workspaces/:id/resume with your workspace API key.

Admin Dashboard

Click "Resume Payouts" on the workspace detail page in the admin panel.

Resume Endpoint

POST
/api/v1/workspaces/:id/resume
curl -X POST https://www.paydirect.com/api/v1/workspaces/{workspaceId}/resume \
  -H "Authorization: Bearer pd_live_abc123..."
// Response (200 OK)
{
  "resumed": true,
  "workspace": "My Agent",
  "previousStreak": 3,
  "previousReason": "3 consecutive payout failures: insufficient balance..."
}

Handling in your agent code:

const res = await fetch(PAYDIRECT_URL + "/api/v1/payouts", { ... });
const data = await res.json();

if (res.status === 503 && data.code === "CIRCUIT_BREAKER_OPEN") {
  // Stop scheduling new payouts
  agent.pausePayouts(data.resumeAction);
  return;
}

if (!res.ok && data.failStreak >= 2) {
  // Approaching circuit breaker threshold
  agent.warn(`Payout failing: ${data.remainingBeforePause} attempts left`);
}
Common Use Cases
When to use the Payouts API
Use CaseTokenExample
Affiliate commissionsUSDCPay affiliates their earned commission each month
Partner revenue shareUSDCPay partners their percentage of revenue
Referral rewardsADAOSend ADAO rewards to users who refer new signups
Token cashoutUSDCConvert internal token balances to USDC payouts
Agent-to-agent paymentsUSDC / ADAOAutonomous agents paying other agents for services
Contractor paymentsUSDC / ETHPay freelancers and contractors in crypto