curl --request PATCH \
--url https://api.citadel.example/api/v1/wallets/{reference} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"tags": [
"vip",
"payouts"
],
"end_user_ref": "user-5120"
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({tags: ['vip', 'payouts'], end_user_ref: 'user-5120'})
};
fetch('https://api.citadel.example/api/v1/wallets/{reference}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.citadel.example/api/v1/wallets/{reference}"
payload = {
"tags": ["vip", "payouts"],
"end_user_ref": "user-5120"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.citadel.example/api/v1/wallets/{reference}"
payload := strings.NewReader("{\n \"tags\": [\n \"vip\",\n \"payouts\"\n ],\n \"end_user_ref\": \"user-5120\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "0199a3c4-7f1e-7b11-9c2e-3f4a5b6c7d8e",
"reference": "user-4211",
"chain": "tron",
"network": "mainnet",
"address": "TC6A9sQD2tCSaR1wcBXZtrvGkFyoLgRHHF",
"status": "active",
"label": "deposit",
"tags": [
"vip",
"payouts"
],
"end_user_ref": "user-5120",
"vault": "0199a3b0-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"created_at": "2026-10-07T12:00:00Z"
}Update a wallet
Changes what you say about a wallet: its tags, its label, and the user it belongs to
(end_user_ref). Send only what changes; a field you leave out keeps its value. tags
replaces the whole list, and [] clears it. Workflows pick wallets by their tags.
Nothing else changes. The address, chain, network, reference and vault are the
wallet itself: a body that sends one of them, or any other field, is refused with 422
and changes nothing. A token sub-account Citadel keeps under a wallet (a TON jetton wallet,
a Solana token account) is not changed on its own: it follows its wallet’s end_user_ref.
A new end_user_ref applies from now on: lists filter on it, and the network-fee top-ups
Citadel sends the wallet carry it. Transactions already made keep the end_user_ref they
were sent with. Your policy engine counts per-user limits on the end_user_ref each
transaction is signed with, not on the wallet’s.
network is required: a reference names a wallet within it. Needs the wallets.write
scope. A wallet outside the key’s vaults answers 404.
curl --request PATCH \
--url https://api.citadel.example/api/v1/wallets/{reference} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"tags": [
"vip",
"payouts"
],
"end_user_ref": "user-5120"
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({tags: ['vip', 'payouts'], end_user_ref: 'user-5120'})
};
fetch('https://api.citadel.example/api/v1/wallets/{reference}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.citadel.example/api/v1/wallets/{reference}"
payload = {
"tags": ["vip", "payouts"],
"end_user_ref": "user-5120"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.citadel.example/api/v1/wallets/{reference}"
payload := strings.NewReader("{\n \"tags\": [\n \"vip\",\n \"payouts\"\n ],\n \"end_user_ref\": \"user-5120\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "0199a3c4-7f1e-7b11-9c2e-3f4a5b6c7d8e",
"reference": "user-4211",
"chain": "tron",
"network": "mainnet",
"address": "TC6A9sQD2tCSaR1wcBXZtrvGkFyoLgRHHF",
"status": "active",
"label": "deposit",
"tags": [
"vip",
"payouts"
],
"end_user_ref": "user-5120",
"vault": "0199a3b0-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"created_at": "2026-10-07T12:00:00Z"
}Authorizations
An API key minted in the console (ctd_…). Authenticates; never authorizes funds. A key
carries scopes (read, wallets.write, transactions.write, events.write,
webhooks.write, policy.write) and may be limited to vaults, to IP addresses, and in
lifetime — see Authentication.
Path Parameters
Your reference for the wallet, or its id.
Query Parameters
The network the data lives on. Required — there is no default network: without it,
422 network_required. A wallet named by its id on another network: 422 network_mismatch.
The network the data lives on. Required wherever it appears in a request — there is no
default network. One your installation does not serve: 422 network_not_served.
mainnet, testnet Body
Response
The wallet as it now reads, the body GET /wallets/{reference} returns.
tron, evm, solana, ton The network the data lives on. Required wherever it appears in a request — there is no
default network. One your installation does not serve: 422 network_not_served.
mainnet, testnet creating — issued, your signer has not derived it yet; active — in circulation;
retired — out of circulation (POST /wallets/{reference}/retire): still watched,
credited, and able to send; failed — your signer could not derive it, it has no address.
creating, active, retired, failed Absent while creating, and on a failed wallet.
The vault the wallet is in.