Product
Custodial Wallet API
Create, list, balance, sign, and send — all wallet operations via the API.
The custodial wallet API lives under /wallets. All routes require Bearer authentication (Dynamic JWT or API key). Wallets are scoped to the authenticated user.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /wallets | Create custodial wallet |
| GET | /wallets | List user's wallets |
| GET | /wallets/:id | Get wallet details |
| GET | /wallets/:id/balance | Get balance (wei) |
| POST | /wallets/:id/sign | Sign message |
| POST | /wallets/:id/send | Send transaction |
Create Wallet
POST /wallets
Authorization: Bearer <jwt_or_api_key>Response (201):
{
"id": "uuid",
"address": "0x...",
"chainId": 11155111,
"createdAt": "2025-03-14T..."
}Chain is Sepolia (11155111). The private key is generated server-side, encrypted with AES-256-GCM, and stored. Only the address and metadata are returned.
Get Balance
GET /wallets/:id/balance
Authorization: Bearer <jwt_or_api_key>Response (200):
{
"balance": "1000000000000000000"
}Balance is returned in wei (string). Use viem's formatEther to display in ETH.
Sign Message
POST /wallets/:id/sign
Authorization: Bearer <jwt_or_api_key>
Content-Type: application/json
{ "msg": "Hello, Web3!" }Response (200):
{
"signedMessage": "0x..."
}Send Transaction
POST /wallets/:id/send
Authorization: Bearer <jwt_or_api_key>
Content-Type: application/json
{ "to": "0x...", "amount": "0.001" }Response (200):
{
"transactionHash": "0x..."
}amountis in ETH (e.g."0.001"). Internally converted to wei via viem'sparseEther.tomust be a valid Ethereum address (viemgetAddressvalidation).- Returns
400 INSUFFICIENT_FUNDSwhen the wallet lacks enough ETH.
Using the Generated Client
@repo/core exposes typed methods for all wallet routes:
import { createClient } from '@repo/core'
const client = createClient({
baseUrl: process.env.NEXT_PUBLIC_API_URL!,
getAuthToken: async () => session?.token,
})
// Create wallet
const { id, address } = await client.walletsCreate()
// List wallets
const { wallets } = await client.walletsList()
// Balance (id from create or list)
const { balance } = await client.wallets.id.balance({ path: { id } })
// Sign
const { signedMessage } = await client.wallets.id.sign({
path: { id },
body: { msg: 'Hello' },
})
// Send
const { transactionHash } = await client.wallets.id.send({
path: { id },
body: { to: '0x...', amount: '0.001' },
})OpenAPI and Scalar
Full schema: /reference/openapi.json. Interactive docs: /reference (Scalar).
Related
- Security Model — Encrypted keys, rate limiting
- API Architecture — Route structure, OpenAPI generation