Transfers
Move funds between wallets with an OTP-confirmed flow — create, confirm, resend the code, cancel, list, or fetch a transfer.
Move funds wallet-to-wallet with a two-step, OTP-confirmed flow. Create a transfer to put it in a PENDING state and email a one-time code to the source wallet owner, then confirm it with that code to settle the balances. Need a fresh code or changed your mind? Resend the code or cancel the pending transfer. List a wallet's transfers or fetch a single one by id.
Authentication: every endpoint on this page acts on a single user's wallets, so it requires that user's token in the Authorization header as a Bearer token. Get one by issuing a token for the user.
Authorization: Bearer USER_TOKENAll requests accept an optional x-trace-id header for request tracking; if omitted, one is generated.
Create Transfer
Start a wallet-to-wallet transfer. Both wallets must exist and be active, they can't be the same wallet, and the source must hold enough available balance in the currency. The transfer lands in a PENDING state and an OTP goes to the source wallet owner's email—nothing moves until you confirm it. The id in the response is what you'll pass to confirm, resend, or cancel.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| destination_wallet_id | string (uuid v4) | Yes | Wallet that receives the funds. Must be active and different from the source wallet. |
| type | string | Yes | Transfer type. One of PAYIN, PAYOUT. |
| purpose | string | Yes | Reason for the transfer (max 255). |
| description | string | Yes | Human-readable description (max 500). |
| currency | string | Yes | Currency code, e.g. USDC (max 10). |
| amount | number | Yes | Amount to transfer. Must be positive and at least 0.000001. |
{
"destination_wallet_id": "34956530-d630-45f3-b5c1-0c3332b80ce5",
"type": "PAYOUT",
"purpose": "Payment goods",
"description": "Transfer to wallet 1",
"currency": "USDC",
"amount": 25000
}Success Response (201 Created):
| Field | Type | Description |
|---|---|---|
| id | string (uuid) | Transfer ID, used to confirm, resend, or cancel. |
| transaction | object | Transfer detail. |
| transaction.created_at | string (ISO 8601) | Creation timestamp. |
| transaction.action | string | Transfer. |
| transaction.currency | string | Currency. |
| transaction.memo | string | Note. |
| transaction.amount | number | Amount. |
| transaction.net_proceeds | number | Net amount. |
| transaction.price | number | Applied price. |
| transaction.fees | number | Fees. |
| transaction.status | string | PENDING / COMPLETED. |
| transaction.description | string | Description. |
| transaction.wallet_sender_id | string (uuid) | Source wallet. |
| transaction.destination_wallet_id | string (uuid) | Destination wallet. |
{
"success": true,
"data": {
"id": "7c9e6f2a-1b3d-4e8f-9a2c-5d6e7f8a9b0c",
"transaction": {
"created_at": "2026-01-15T10:30:00.000Z",
"action": "Transfer",
"currency": "USDC",
"memo": "Transfer to wallet 1",
"amount": 25000,
"net_proceeds": 25000,
"price": 1,
"fees": 0,
"status": "PENDING",
"description": "Transfer to wallet 1",
"wallet_sender_id": "f4057807-52cf-4083-9ecb-283ef354fb2b",
"destination_wallet_id": "34956530-d630-45f3-b5c1-0c3332b80ce5"
}
},
"meta": {
"timestamp": "2026-01-15T10:30:00.000Z",
"version": "v1",
"trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
}
}curl -X POST "https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer USER_TOKEN" \
-d '{
"destination_wallet_id": "34956530-d630-45f3-b5c1-0c3332b80ce5",
"type": "PAYOUT",
"purpose": "Payment goods",
"description": "Transfer to wallet 1",
"currency": "USDC",
"amount": 25000
}'Errors: 400 invalid fields, same source and destination, or insufficient balance · 401 invalid key · 404 source or destination wallet not found or inactive · 500 server error.
Confirm Transfer
Settle a PENDING transfer with the code emailed to the source wallet owner. On a valid code the balances move and the transfer's status becomes COMPLETED. The code expires after 10 minutes—resend it if it lapses. After 3 wrong codes the transfer is blocked and returns 403 Forbidden.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| code | string | Yes | The 6-digit one-time code sent to the wallet owner's email. |
{
"code": "602391"
}Success Response (200 OK) — same object as Create, with transaction.status set to COMPLETED:
{
"success": true,
"data": {
"id": "7c9e6f2a-1b3d-4e8f-9a2c-5d6e7f8a9b0c",
"transaction": {
"created_at": "2026-01-15T10:30:00.000Z",
"action": "Transfer",
"currency": "USDC",
"memo": "Transfer to wallet 1",
"amount": 25000,
"net_proceeds": 25000,
"price": 1,
"fees": 0,
"status": "COMPLETED",
"description": "Transfer to wallet 1",
"wallet_sender_id": "f4057807-52cf-4083-9ecb-283ef354fb2b",
"destination_wallet_id": "34956530-d630-45f3-b5c1-0c3332b80ce5"
}
},
"meta": {
"timestamp": "2026-01-15T10:31:00.000Z",
"version": "v1",
"trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
}
}curl -X POST "https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers/{{transferID}}/confirm" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer USER_TOKEN" \
-d '{
"code": "602391"
}'Errors: 400 invalid or expired code, or transfer already completed/cancelled · 401 invalid key · 403 transfer blocked after 3 failed attempts · 404 transfer not found · 500 server error.
Resend Code
Resend the verification code for a PENDING transfer. This generates a fresh code, resets the failed-attempt counter, and emails the new code to the source wallet owner. Use it when the original code expired or never arrived. Already-completed or cancelled transfers can't be re-verified.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Verification method. Must be email. |
{
"type": "email"
}Success Response (200 OK):
| Field | Type | Description |
|---|---|---|
| status | string | SENDED. |
| verification_at | string (ISO 8601) | When the code was resent. |
{
"success": true,
"data": {
"status": "SENDED",
"verification_at": "2026-01-15T10:35:00.000Z"
},
"meta": {
"timestamp": "2026-01-15T10:35:00.000Z",
"version": "v1",
"trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
}
}curl -X POST "https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers/{{transferID}}/verification/codes" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer USER_TOKEN" \
-d '{
"type": "email"
}'Errors: 400 transfer already completed or cancelled · 401 invalid key · 404 transfer not found · 500 server error.
Cancel Transfer
Cancel a PENDING transfer before it's confirmed. The transfer moves to a CANCELLED state and can no longer be confirmed. A transfer that's already completed or already cancelled can't be cancelled again.
DELETE https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers/{{transferID}}Success Response (204 No Content) — empty body, no JSON.
curl -X DELETE "https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers/{{transferID}}" \
-H "Authorization: Bearer USER_TOKEN"Errors: 400 transfer already completed or cancelled · 401 invalid key · 404 transfer not found · 500 server error.
List Transfers
List a wallet's transfers, most recent first, with pagination under meta.pagination. Each item matches the Create response.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| filter[limit] | number | No | Items per page (default: 20, max: 100). |
| filter[skip] | number | No | Offset. |
| filter[sort] | string | No | Sort by date: ASC or DESC (default DESC). |
| filter[where][status] | string | No | Normalized status: PENDING, COMPLETED, or CANCELLED. |
GET https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers?filter[limit]=20&filter[skip]=0Success Response (200 OK) — data is an array of transfers:
{
"success": true,
"data": [
{
"id": "7c9e6f2a-1b3d-4e8f-9a2c-5d6e7f8a9b0c",
"transaction": {
"created_at": "2026-01-15T10:30:00.000Z",
"action": "Transfer",
"currency": "USDC",
"memo": "Transfer to wallet 1",
"amount": 25000,
"net_proceeds": 25000,
"price": 1,
"fees": 0,
"status": "COMPLETED",
"description": "Transfer to wallet 1",
"wallet_sender_id": "f4057807-52cf-4083-9ecb-283ef354fb2b",
"destination_wallet_id": "34956530-d630-45f3-b5c1-0c3332b80ce5"
}
}
],
"meta": {
"timestamp": "2026-01-15T10:30:00.000Z",
"version": "v1",
"trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f",
"pagination": {
"records": { "skip": 0, "has_next": false, "has_previous": false, "total": 1, "limit": 20 },
"navigation": {
"first": "/wallets/{walletID}/transfers?filter[limit]=20&filter[skip]=0",
"last": "/wallets/{walletID}/transfers?filter[limit]=20&filter[skip]=0",
"previous": null,
"next": null
}
}
}
}curl "https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers?filter[limit]=20&filter[skip]=0" \
-H "Authorization: Bearer USER_TOKEN"Errors: 401 invalid key · 404 wallet not found · 500 server error.
Get Transfer
Fetch a single transfer by its id. The response matches the Create item.
GET https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers/{{transferID}}Success Response (200 OK):
{
"success": true,
"data": {
"id": "7c9e6f2a-1b3d-4e8f-9a2c-5d6e7f8a9b0c",
"transaction": {
"created_at": "2026-01-15T10:30:00.000Z",
"action": "Transfer",
"currency": "USDC",
"memo": "Transfer to wallet 1",
"amount": 25000,
"net_proceeds": 25000,
"price": 1,
"fees": 0,
"status": "COMPLETED",
"description": "Transfer to wallet 1",
"wallet_sender_id": "f4057807-52cf-4083-9ecb-283ef354fb2b",
"destination_wallet_id": "34956530-d630-45f3-b5c1-0c3332b80ce5"
}
},
"meta": {
"timestamp": "2026-01-15T10:30:00.000Z",
"version": "v1",
"trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
}
}curl "https://api.sandbox.sovera.io/sovx/v1/wallets/{{walletID}}/transfers/{{transferID}}" \
-H "Authorization: Bearer USER_TOKEN"Errors: 401 invalid key · 404 wallet or transfer not found · 500 server error.
Next Steps
- Confirm a transfer you just created with the code from the owner's email.
- Review a wallet's transactions to see settled transfers.