Vencura
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

MethodPathDescription
POST/walletsCreate custodial wallet
GET/walletsList user's wallets
GET/wallets/:idGet wallet details
GET/wallets/:id/balanceGet balance (wei)
POST/wallets/:id/signSign message
POST/wallets/:id/sendSend 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..."
}
  • amount is in ETH (e.g. "0.001"). Internally converted to wei via viem's parseEther.
  • to must be a valid Ethereum address (viem getAddress validation).
  • Returns 400 INSUFFICIENT_FUNDS when 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).

On this page