v0.1 — Phase 1

Transfer lifecycle

  1. Agent submitsPOST /v1/transfers with recipient, amount, memo, idempotency key
  2. Policy check — ClawdGo validates against the account's on-chain spending policy. If rejected, returns 402 and fires a transfer.rejected webhook.
  3. Signing — ClawdGo constructs and signs the Solana transaction using the PDA authority
  4. On-chain submission — transaction submitted to Solana. Typical finality: <400ms
  5. Response — returns tx_signature, status: "settled", and settled_at timestamp
POST /v1/transfers

Initiate a USDC transfer from an agent account. Can be called with an operator key or an agent-scoped key.

Request body

FieldTypeRequiredDescription
fromstringrequiredSource account_id. Must belong to the authenticated operator.
tostringrequiredRecipient. Either a ClawdGo account_id or a valid Solana USDC SPL token account address.
amountstringrequiredUSDC amount as a decimal string. E.g., "25.00". Minimum: "0.000001".
idempotency_keystringrequiredUnique key for this transfer. Re-submitting the same key returns the original response. Max 128 chars.
memostringoptionalOptional memo string attached to the transfer. Required if the account policy has require_memo: true. Max 200 chars.

Response — 200 OK (settled)

json
{
  "transfer_id": "txn_02m9n5y3q7wpuv8ab",
  "status": "settled",
  "from": "acct_01j8k4x9p2qrst7yz",
  "to": "9xTz4KqR8mYvPn3SdFgHj6WbCeAuLo1XkQi5NtZpMwV",
  "amount_usdc": "25.000000",
  "memo": "Invoice #1042 — vendor payment",
  "idempotency_key": "inv-1042-2024-01-15",
  "tx_signature": "5j4Kz9qXm2nWpRt8vYbFdCgHs3Ae7UoLkNi6ZwQxPmV4rBe...",
  "solana_explorer_url": "https://explorer.solana.com/tx/5j4Kz9qX...",
  "settled_at": "2024-01-15T10:41:02Z"
}

Response — 402 Payment Required (policy rejected)

json
{
  "error": "policy_violation",
  "message": "Transfer amount 750.00 exceeds max_single_transfer limit of 500.00 USDC",
  "transfer_id": "txn_02m9n5y3q7wpuv8ab",
  "status": "rejected"
}
GET /v1/transfers/:id

Retrieve a specific transfer by its ID. Useful for polling transfer status or for audit purposes.

Error codes

HTTPError codeMeaning
402policy_violationTransfer rejected by spending policy.
402account_pausedAccount has paused: true in policy.
402insufficient_balanceSource account has insufficient USDC balance.
409idempotency_conflictIdempotency key reused with different parameters.
422invalid_recipientRecipient address is not a valid USDC SPL token account.
503solana_unavailableSolana RPC unavailable. Retry with backoff.

Idempotency

All transfers require a unique idempotency_key. If you submit a request with a key you have used before:

  • If the original transfer succeeded: returns the original settled response. No duplicate transfer is made.
  • If the original transfer failed (non-policy failure): retries the transfer.
  • If you use the same key with different parameters: returns 409 idempotency_conflict.

Keys expire after 24 hours. After expiry, the key can be reused freely.

Use deterministic keys. Good keys are derived from the underlying business event: invoice-{id}-{date}, task-{task_id}-reward. Avoid random UUIDs unless you store them durably — a random key on retry creates a duplicate transfer.