Status: Implemented
This page describes the live public API for governed USDC payouts on Solana.
These routes are implemented in apps/api/src/rest/routers/agent-spend-v1.ts.
#Scope
- Chain:
solana - Asset:
USDC - Mint:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v - Decimals:
6 - Auth: Dino spending keys (
din_...) and team API keys (dba_..., legacymid_...)
For devnet wallet testing, use Crypto devnet guide. Dino's sandbox faucet is USD-only and does not mint SOL or USDC.
#AI agents: natural language → tool → Dino
Same pattern as Connecting AI Agents to Dino: the model proposes the payout; your server or MCP tool injects the bearer token and Idempotency-Key on POST /v1/crypto/payout-intents. Never paste spending keys into chat or prompts.
Example tools: create_crypto_payout_intent(amount_atomic, recipient_address, reason?, merchant_name?, idempotency_key?, metadata?) → POST /v1/crypto/payout-intents; get_crypto_payout_intent(id) → GET /v1/crypto/payout-intents/:id. Approve / decline require a team API key or dashboard (spending keys cannot approve).
For MCP, keep calling these REST routes from a trusted process. See Assistant and MCP Integration.
#Endpoints
GET /v1/crypto/capabilities— Dino spending key or team API keyGET /v1/crypto/balance— team API key onlyPOST /v1/crypto/payout-intents— Dino spending key or team API keyGET /v1/crypto/payout-intents— Dino spending key or team API keyGET /v1/crypto/payout-intents/:id— Dino spending key or team API keyPOST /v1/crypto/payout-intents/:id/cancel— Dino spending key or team API key; only while status isneeds_approvalPOST /v1/crypto/payout-intents/:id/approve— team API key onlyPOST /v1/crypto/payout-intents/:id/decline— team API key onlyGET /v1/crypto/recipients— team API key onlyPOST /v1/crypto/recipients— team API key onlyPATCH /v1/crypto/recipients/:id— team API key only
#Execution Modes
- Create supports
workspace_custodialonly (workspace USDC custody). - Read models may still return
user_walletfor older linked-wallet spend rows (compatibility only; not advertised for new integrations).
#Payout Lifecycle
Returned status values can include:
needs_approvalapprovedawaiting_signaturesubmittedcompletedfaileddeclinedcancelledexpired
#Auth notes
- Team API keys must include the matching spend scope:
spend.readfor reads,spend.writefor writes. - Dino spending keys cannot approve, decline, or manage saved recipients.
GET /v1/crypto/balanceis workspace-scoped and requires a team API key.GET /v1/crypto/balanceis read-only and returns zeroed balances when the workspace has no crypto wallet yet.
#Example: create payout intent
curl -sS -X POST "https://api.dino.id/v1/crypto/payout-intents" \
-H "Authorization: Bearer YOUR_DINO_SPEND_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"amount_atomic": "12500000",
"chain": "solana",
"asset_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"recipient_address": "7xKXtg2CWJ1Axn7x2LQ5vF7oWQ8B8yrW5K8J2R6dJ1QH",
"merchant_name": "Payout recipient",
"reason": "Affiliate payout",
"execution_mode": "workspace_custodial",
"metadata": { "invoice_id": "inv_123" }
}'
#Example: read payout status
curl -sS "https://api.dino.id/v1/crypto/payout-intents/cpi_123" \
-H "Authorization: Bearer YOUR_DINO_SPEND_KEY"
Current response fields include:
pending_reasonfailure_reasontx_signaturesubmitted_atidempotency_key
#Runnable example
The monorepo includes a REST-only quickstart that reads capabilities, creates a payout intent, and reads it back:
cd examples/crypto-payout-quickstart
cp .env.example .env
bun run start
See examples/crypto-payout-quickstart.
#List Filters
GET /v1/crypto/payout-intents currently supports:
statuslimitcursorcreated_afteragent_account_ididempotency_keymerchant_name_prefixexecution_mode
#Error Model
Common error.code values include:
invalid_requestinsufficient_scopenot_foundpolicy_declinedidempotency_key_conflict
#Webhooks
Crypto payout webhooks currently include:
crypto_payout.createdcrypto_payout.needs_approvalcrypto_payout.approvedcrypto_payout.submittedcrypto_payout.confirmedcrypto_payout.failedcrypto_payout.expiredcrypto_payout.cancelledcrypto_payout.declined
Payload note: event types are crypto-specific, but the body may still use the generic data.spend shape (same fields as spend_request.* webhooks — e.g. amount_cents, no chain, mint, recipient, or tx_signature) until an explicitly crypto-shaped payload ships or the generic shape is documented as the supported contract.
Integration-complete checklist: remove the unsafe unknown-status → approved fallback in mapCryptoIntentStatus (agent-spend-v1.ts), and ship crypto-shaped webhook payloads (or formally version/document the generic envelope) before promising crypto fields to external integrators.