Vaultly API Reference
Multi-currency wallet API with USD-based ledger. Transfer, withdraw, query balances and track withdrawal status — all from four JSON endpoints.
Introduction
Vaultly is a wallet-as-a-service platform. After registering, every user has a personal panel where they can mint API keys (each key represents one wallet) and move funds between wallets, transfer to other users, or submit withdrawal requests. Wallet balances are never stored — they are always computed from the transaction ledger.
All endpoints accept and return application/json. All money values are returned as decimal strings to avoid floating-point drift in JavaScript clients.
Authentication
Every API request authenticates per-call. There are no tokens or sessions. Each request must include the four fields below in its JSON body:
| Field | Type | Description |
|---|---|---|
api_key | string | The source wallet's API key (format vk_live_…). |
email | string | The owner's email address. |
password_sha512 | string | Lower-case hex SHA-512 of the user's plaintext password (128 chars). |
currency | string | Upper-case currency code, e.g. USD, AED, IRR, USDT. Required for all endpoints except /withdraw/status. |
The server validates these in a fixed order. Wrong email and wrong password return the same response (INVALID_CREDENTIALS) so attackers cannot enumerate accounts.
Error format
{
"ok": false,
"error": "INSUFFICIENT_FUNDS",
"message": "Insufficient funds"
}
| Code | HTTP | Meaning |
|---|---|---|
INVALID_CREDENTIALS | 401 | Email not found, or password SHA-512 mismatch. |
WALLET_NOT_FOUND | 404 | The given api_key does not exist. |
WALLET_NOT_OWNED | 403 | The wallet is not owned by the supplied email. |
WALLET_DISABLED | 403 | The wallet has been disabled by its owner or by an admin. |
CURRENCY_NOT_SUPPORTED | 422 | Unknown currency code. |
DESTINATION_NOT_FOUND | 404 | (transfer) Destination wallet not found. |
DESTINATION_DISABLED | 403 | (transfer) Destination wallet disabled. |
SAME_WALLET | 422 | (transfer) Source equals destination. |
INSUFFICIENT_FUNDS | 422 | Source wallet does not have enough USD to cover the converted amount. |
INVALID_AMOUNT | 422 | Amount is zero, negative, or malformed. |
WITHDRAWAL_NOT_FOUND | 404 | (/withdraw/status) Transaction code is unknown or not owned by this wallet. |
WITHDRAWAL_DESTINATION_NOT_CONFIGURED | 500 | (server) ADMIN_WITHDRAWAL_API_KEY not configured. |
Transfer
Move funds from one wallet to any other active wallet — yours or someone else's. Two ledger rows are created in a single DB transaction, sharing one ULID transaction code.
Request body
All authentication fields, plus:
| Field | Type | Description |
|---|---|---|
destination_api_key | string | The receiving wallet's key. |
amount | string | Decimal string in the chosen currency, e.g. "36725.00000000". |
description | string? | Optional, max 1000 chars. |
curl -X POST https://vaultly.plus/api/v1/transfer \
-H 'Content-Type: application/json' \
-d '{
"api_key": "vk_live_...",
"email": "alice@example.com",
"password_sha512": "<128-hex>",
"currency": "AED",
"destination_api_key": "vk_live_...",
"amount": "36725.00000000",
"description": "rent"
}'
$client = new \GuzzleHttp\Client();
$res = $client->post('https://vaultly.plus/api/v1/transfer', [
'json' => [
'api_key' => 'vk_live_...',
'email' => 'alice@example.com',
'password_sha512' => hash('sha512', $plaintextPassword),
'currency' => 'AED',
'destination_api_key' => 'vk_live_...',
'amount' => '36725.00000000',
'description' => 'rent',
],
]);
echo $res->getBody();
const res = await fetch('https://vaultly.plus/api/v1/transfer', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'vk_live_...',
email: 'alice@example.com',
password_sha512: '',
currency: 'AED',
destination_api_key: 'vk_live_...',
amount: '36725.00000000'
})
});
console.log(await res.json());
{
"ok": true,
"transaction_code": "01HXY...",
"amount_in_currency": "36725.00000000",
"currency": "AED",
"amount_in_usd": "10000.00000000"
}
With AED.rate = 3.6725, sending 36725 AED debits the source by exactly 10000 USD and credits the destination by exactly 10000 USD.
Withdraw
Submit a withdrawal request. Identical to transfer except the destination wallet is fixed (the platform's withdrawal wallet) and a withdrawals record is created in pending status. An admin then changes its status to done, blocked, or refunded. Use /withdraw/status to poll the result.
Request body
All authentication fields, plus:
| Field | Type | Description |
|---|---|---|
amount | string | Decimal string in the chosen currency. |
destination | string | Required. External payout target — wallet address, IBAN, card number, etc. (4–500 chars). Stored on the user-leg transaction's user_description. |
description | string? | Optional user note (max 1000 chars), appended after the destination in user_description. |
curl -X POST https://vaultly.plus/api/v1/withdraw \
-H 'Content-Type: application/json' \
-d '{
"api_key": "vk_live_...",
"email": "alice@example.com",
"password_sha512": "<128-hex>",
"currency": "USDT",
"amount": "250.00000000",
"destination": "0xabc1234567890DEADBEEFcafe0000000000111122",
"description": "monthly payout"
}'
{
"ok": true,
"transaction_code": "01HZX2K8M9P3R7T2V5W8Y0Z4B6",
"amount_in_currency": "250.00000000",
"currency": "USDT",
"amount_in_usd": "250.00000000"
}
Save the transaction_code — you'll need it to query /withdraw/status.
Withdraw status
Look up the current status of a previously-submitted withdrawal. The wallet that originally submitted the withdrawal is the only one that can read it. The currency field is not required for this endpoint.
Request body
| Field | Type | Description |
|---|---|---|
api_key | string | The wallet that submitted the withdrawal. |
email | string | Owner email. |
password_sha512 | string | SHA-512 hex of the password. |
transaction_code | string | The transaction_code returned by /withdraw. |
curl -X POST https://vaultly.plus/api/v1/withdraw/status \
-H 'Content-Type: application/json' \
-d '{
"api_key": "vk_live_...",
"email": "alice@example.com",
"password_sha512": "<128-hex>",
"transaction_code": "01HZX2K8M9P3R7T2V5W8Y0Z4B6"
}'
{
"ok": true,
"transaction_code": "01HZX2K8M9P3R7T2V5W8Y0Z4B6",
"status": "done",
"admin_description": "Paid via TRC-20, tx 0x9f2a…",
"user_description": "Destination: 0xabc1234567890DEADBEEFcafe0000000000111122\nmonthly payout",
"amount_in_currency": "250.00000000",
"currency": "USDT",
"created_at": "2026-05-04T10:14:00+00:00",
"updated_at": "2026-05-04T11:02:18+00:00"
}
Status values
| Status | Meaning |
|---|---|
pending | Awaiting admin review. admin_description will be null. |
done | Admin marked the withdrawal as completed. admin_description typically contains the on-chain tx hash or bank reference. |
blocked | Admin rejected the withdrawal but did not refund the user. Funds remain in the platform's withdrawal wallet. |
refunded | Admin refunded the user's wallet. admin_description includes the refund transaction code. |
Balance
Return the wallet's balance in USD plus the equivalent in any supported currency. This endpoint never writes a transaction.
curl -X POST https://vaultly.plus/api/v1/balance \
-H 'Content-Type: application/json' \
-d '{
"api_key": "vk_live_...",
"email": "alice@example.com",
"password_sha512": "<128-hex>",
"currency": "AED"
}'
{
"ok": true,
"balance_in_currency": "36725.00000000",
"currency": "AED",
"balance_in_usd": "10000.00000000"
}
Rates
Return every supported currency together with its current USD rate. Public endpoint — no authentication required. The rate_to_usd field is the multiplier such that amount_in_currency = amount_in_usd × rate_to_usd.
curl https://vaultly.plus/api/v1/rates
{
"ok": true,
"base": "USD",
"count": 162,
"rates": [
{ "code": "USDC", "type": "crypto", "rate_to_usd": "1.00000000", "updated_at": "2026-05-04T08:00:00+00:00" },
{ "code": "USDT", "type": "crypto", "rate_to_usd": "1.00000000", "updated_at": "2026-05-04T08:00:00+00:00" },
{ "code": "AED", "type": "fiat", "rate_to_usd": "3.67250000", "updated_at": "2026-05-04T11:00:12+00:00" },
{ "code": "EUR", "type": "fiat", "rate_to_usd": "0.92810000", "updated_at": "2026-05-04T11:00:12+00:00" },
{ "code": "GBP", "type": "fiat", "rate_to_usd": "0.79420000", "updated_at": "2026-05-04T11:00:12+00:00" },
{ "code": "IRR", "type": "fiat", "rate_to_usd": "905000.00000000", "updated_at": "2026-05-04T11:14:33+00:00" },
{ "code": "USD", "type": "fiat", "rate_to_usd": "1.00000000", "updated_at": "2026-05-04T11:00:12+00:00" }
/* …rest of currencies… */
]
}
Currency codes
All currency codes are upper-case ISO 4217 (fiat) or ticker symbols (crypto). Vaultly supports the full set returned by exchangerate-api.com (≈160 fiat currencies) plus a handful of manually-pegged crypto stablecoins. Use GET /api/v1/rates for the canonical, live list.
Highlights
| Code | Type | Source | Refresh |
|---|---|---|---|
USD | fiat | Base — always 1.00000000 | — |
EUR, GBP, AED, JPY, CNY, TRY, INR, SAR, CAD, AUD, CHF… (~160 codes) | fiat | exchangerate-api.com | hourly |
IRR | fiat | Wallex (USDT/TMN price × 10) | every 15 min |
USDT, USDC | crypto | Manually pegged to 1.00000000 | manual |
Sample of supported fiat codes: AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHF, CLP, CNY, COP, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRK, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, STN, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, UYU, UZS, VES, VND, VUV, WST, XAF, XCD, XDR, XOF, XPF, YER, ZAR, ZMW, ZWL — and more added by the upstream provider.
Rate limits
The API enforces 60 requests per minute per IP across all endpoints. Excess traffic returns HTTP 429.
Changelog
- v1.2 — added
GET /api/v1/rates; documented full ISO 4217 fiat coverage (~160 currencies) plus pegged stablecoins. - v1.1 — added
/api/v1/withdraw/status;/withdrawnow requires adestinationfield (wallet address, IBAN, etc.) which is persisted on the user-leg transaction. - v1.0 — initial release with transfer, withdraw, balance.