# Manage accounts with the API

Use this page for account operations with the Ripple Custody (Vault) API. For UI procedures, see [Manage accounts in the UI](/pt-br/products/custody/accounts-and-assets/accounts/manage-accounts-ui). For the account model, see [Accounts](/pt-br/products/custody/accounts-and-assets/accounts/overview).

## Account actions

| Action | API procedure | API reference |
|  --- | --- | --- |
| Create an account | [Create an account with the API](#create-an-account-with-the-api) | [Propose an intent](/pt-br/products/custody/reference/api/openapi/intents/createintent), [Perform a dry run](/pt-br/products/custody/reference/api/openapi/intents/intentdryrun) |
| View account details | [View account details](#view-account-details) | [List accounts](/pt-br/products/custody/reference/api/openapi/accounts/getaccounts), [Get account details](/pt-br/products/custody/reference/api/openapi/accounts/getaccount), [Retrieve account address](/pt-br/products/custody/reference/api/openapi/accounts/getaccountaddress) |
| Update account details | [Update account details](#update-account-details) | [Propose an intent](/pt-br/products/custody/reference/api/openapi/intents/createintent), [Perform a dry run](/pt-br/products/custody/reference/api/openapi/intents/intentdryrun) |
| Add ledgers to an account | [Add ledgers to an account](#add-ledgers-to-an-account) | [Propose an intent](/pt-br/products/custody/reference/api/openapi/intents/createintent), [Perform a dry run](/pt-br/products/custody/reference/api/openapi/intents/intentdryrun) |
| Lock or unlock an account | [Lock or unlock an account](#lock-or-unlock-an-account) | [Propose an intent](/pt-br/products/custody/reference/api/openapi/intents/createintent), [Perform a dry run](/pt-br/products/custody/reference/api/openapi/intents/intentdryrun) |
| Check account balances | [Check account balances](#check-account-balances) | [Get account balances](/pt-br/products/custody/reference/api/openapi/accounts/getaccountbalances) |
| Force an account balance update | [Force an account balance update](#force-an-account-balance-update) | [Update account balances forcefully](/pt-br/products/custody/reference/api/openapi/accounts/forceupdateaccountbalances) |
| Configure compliance settings | [Configure compliance settings](#configure-compliance-settings) | [Get compliance configuration](/pt-br/products/custody/reference/api/openapi/accounts/getcomplianceconfiguration), [Set compliance configuration](/pt-br/products/custody/reference/api/openapi/accounts/upsertcomplianceconfiguration), [List compliance configurations](/pt-br/products/custody/reference/api/openapi/accounts/listcomplianceconfigurations) |


Account changes use the standard intent flow. For signing, approval, and status checks, see [Manage intents and approvals](/pt-br/products/custody/governance/intents/manage-intents-and-approvals), [Sign intents](/pt-br/products/custody/identity-and-access/authentication/authenticate-api-requests#signing-intents-state-mutation-operations), and [Check updates](/pt-br/products/custody/governance/intents/manage-intents-and-approvals#check-state-with-the-api).

The SDK examples on this page assume a configured client. See [Integrate via SDK](/pt-br/products/custody/get-started/integrate-via-sdk). The SDK methods build, sign, and submit the intent, and then return a pending intent that you can wait on.

## Common operations

| Operation | Intent type | Use when |
|  --- | --- | --- |
| Create account | `v0_CreateAccount` | Register a new account for one or more ledgers. |
| Update account | `v0_UpdateAccount` | Change mutable account details such as alias, description, or custom properties. |
| Add account ledgers | `v0_AddAccountLedgers` | Add compatible ledgers to an existing account. |
| Lock account | `v0_LockAccount` | Prevent outbound activity for an account. |
| Unlock account | `v0_UnlockAccount` | Re-enable a locked account. |


## Create an account with the API

Create an account by submitting a `v0_CreateAccount` intent.

API reference:

```http
POST /v1/intents/dry-run
POST /v1/intents
```

The examples in this section show the intent `payload` block. When you dry run or propose the intent, include this payload inside the standard request body with the intent ID in `id` for dry runs or `request.id` for signed proposals. For the full request shapes, see [Intent request structure](/pt-br/products/custody/governance/intents/intent-request-structure).

Before you create an account, prepare:

| Prerequisite | Additional information |
|  --- | --- |
| Vault ID | [List vaults](/pt-br/products/custody/reference/api/openapi/vaults/getvaults) |
| Key strategy | Use the supported derivations returned by [Get vault details](/pt-br/products/custody/reference/api/openapi/vaults/getvault). |
| Ledger IDs | Use [List ledgers](/pt-br/products/custody/reference/api/openapi/ledgers/getledgers). |
| New IDs | Prepare an account ID and intent ID in standard UUID format. |


The following examples create an account:

API
```json
{
  "payload": {
    "id": "3b6d578e-7396-495d-8392-61b7ede174b3",
    "alias": "btc-zero",
    "providerDetails": {
      "vaultId": "00000000-0000-0000-0000-000000000000",
      "keyStrategy": "VaultHard",
      "type": "Vault"
    },
    "ledgerIds": ["bitcoin-testnet"],
    "lock": "Unlocked",
    "description": "a bitcoin account for testing",
    "customProperties": {
      "testnet_account": "true"
    },
    "type": "v0_CreateAccount"
  }
}
```

TypeScript
```typescript
import { vaultProvider } from '@ripple/ripple-custody-client-lib-typescript';

const pending = await client.accounts.create(
  'btc-zero',
  vaultProvider({ vaultId, keyStrategy: 'VaultHard' }),
  {
    ledgerIds: ['bitcoin-testnet'],
    description: 'a bitcoin account for testing',
    customProperties: { testnet_account: 'true' },
  },
);
await pending.waitAccepted(30_000);
await pending.waitExecuted(120_000);
```

The SDK generates the account ID unless you pass `id`. Pass `targetDomainId` to create the account in a domain other than the configured one.

Java
```java
PendingIntent pending = client.accounts().create(
    domainId,
    null, // the SDK generates the account ID
    "btc-zero",
    Builders.vaultProvider(vaultId, CoreAccountKeyStrategy.VAULT_HARD),
    List.of("bitcoin-testnet"),
    Map.of("testnet_account", "true"));
pending.waitAccepted(60);
pending.waitExecuted(120);
```

The Java `create` method doesn't take a description. Timeouts are in seconds.

Python
```python
from ripple_custody_client_lib_python import vault_provider

pending = client.accounts.create(
    "btc-zero",
    provider_details=vault_provider(vault_id=vault_id, key_strategy="VaultHard"),
    ledger_ids=["bitcoin-testnet"],
    description="a bitcoin account for testing",
    custom_properties={"testnet_account": "true"},
)
pending.wait_accepted(timeout=30)
pending.wait_executed(timeout=120)
```

For a complete program, see example `12` (account lifecycle). To get the examples, see [Examples](/pt-br/products/custody/developer-experience/sdks#examples).

Field notes:

| Field | Notes |
|  --- | --- |
| `providerDetails` | Vault and key strategy details. For more information, see [Key derivation](/pt-br/products/custody/accounts-and-assets/accounts/account-key-derivation-and-ledger-compatibility). |
| `ledgerIds` | One or more ledger IDs associated with the account. |
| `lock` | Use `Locked` to create an inactive account and unlock it later. |
| `type` | `v0_CreateAccount`. |


Ledger-specific account limitations
Accounts you create on the Hedera ledger follow a specific account initialization process. For more information, see [Initialize Hedera accounts](/pt-br/products/custody/accounts-and-assets/accounts/ledger-specific-settings/hedera-api).

Accounts you create on the Algorand ledger can only be used in offline mode on testnet and mainnet. They are in offline mode so cannot be used for consensus, although you can still use them to send and receive funds.

## View account details

Use account read operations to find account IDs, retrieve account details, and retrieve ledger addresses.

API reference:

```http
GET /v1/domains/{domainId}/accounts
GET /v1/domains/{domainId}/accounts/{accountId}
GET /v1/domains/{domainId}/accounts/{accountId}/addresses/{accountAddressId}
```

The following examples list accounts, get one account, and get its addresses:

API
1. If you do not know the account ID, call [List accounts](/pt-br/products/custody/reference/api/openapi/accounts/getaccounts). You can filter accounts by criteria such as ledger ID.
2. Call [Get account details](/pt-br/products/custody/reference/api/openapi/accounts/getaccount) with the account ID.
3. To retrieve a specific account address, call [Retrieve account address](/pt-br/products/custody/reference/api/openapi/accounts/getaccountaddress).


TypeScript
```typescript
// List accounts in the configured domain
const page = await client.accounts.list({ autoPaginate: true });
const accounts = Array.isArray(page) ? page : page.items;
for (const a of accounts) {
  console.log(a.data.id, a.data.alias);
}

// Get account details and one account address
const account = await client.accounts.get(accountId);
const address = await client.accounts.getAddress(accountId, accountAddressId);
console.log(account?.alias, address.data.ledgerId, address.data.address);
```

Java
```java
// List accounts in the domain
client.accounts().stream(domainId)
    .forEach(a -> System.out.println(a.getData().getId() + " " + a.getData().getAlias()));

// Get account details and list its addresses
CoreApiAccount account = client.accounts().get(domainId, accountId);
CoreAddressesCollection addresses = client.accounts().getAddresses(domainId, accountId, 10, null);
addresses.getItems().forEach(a ->
    System.out.println(a.getData().getLedgerId() + " " + a.getData().getAddress()));
```

The Java SDK has no method to fetch a single address by its ID. List the account addresses instead.

Python
```python
# List accounts in the configured domain
for a in client.accounts.list(auto_paginate=True):
    print(a.data.id, a.data.alias)

# Get account details and one account address
account = client.accounts.get(account_id)
address = client.accounts.get_address(account_id, account_address_id)
print(account.alias, address.data.ledger_id, address.data.address)
```

Depending on the account type, account details include:

| Account type | Details returned |
|  --- | --- |
| Single-ledger account | The ledger address in the `ledgerId` field. In `providerDetails`, `keyInformation` contains public key data and the derivation path for the ledger. |
| Multi-ledger account | The list of ledgers in `additionalDetails.ledgers`, including each ledger status. The `keys` array contains public key data and derivation paths for supported ledger curves. |


Multi-ledger account ledger statuses include:

| Status | Meaning |
|  --- | --- |
| `Activated` | The ledger is active for the account. |
| `Activating` | The ledger is currently being activated for the account. |
| `Available` | The ledger is available to be activated for the account. |
| `Unavailable` | The ledger is not compatible with the account's vault or key strategy. |


## Update account details

Use `v0_UpdateAccount` to update mutable account details such as alias, description, and custom properties.

API reference:

```http
POST /v1/intents/dry-run
POST /v1/intents
```

Submit an update intent with the current account reference:

API
```json
{
  "payload": {
    "reference": {
      "id": "3b6d578e-7396-495d-8392-61b7ede174b3",
      "revision": 2
    },
    "alias": "btc-zero",
    "description": "Updated account description",
    "customProperties": {},
    "type": "v0_UpdateAccount"
  }
}
```

TypeScript
```typescript
// The SDK fetches the current revision when you omit `revision`
const pending = await client.accounts.update(accountId, 'btc-zero', {
  description: 'Updated account description',
  customProperties: {},
});
await pending.waitAccepted(30_000);
await pending.waitExecuted(120_000);
```

Java
```java
// The account entity carries the current revision
CoreApiAccount account = client.accounts().get(domainId, accountId);
PendingIntent pending = client.accounts().update(
    domainId, account, "btc-zero", "Updated account description", Map.of());
pending.waitAccepted(60);
pending.waitExecuted(120);
```

Python
```python
# The SDK fetches the current revision when you omit revision
pending = client.accounts.update(
    account_id,
    "btc-zero",
    description="Updated account description",
    custom_properties={},
)
pending.wait_accepted(timeout=30)
pending.wait_executed(timeout=120)
```

## Add ledgers to an account

Use `v0_AddAccountLedgers` to add compatible ledgers to an existing account.

API reference:

```http
POST /v1/intents/dry-run
POST /v1/intents
```

Submit an add-ledgers intent with the current account reference:

API
```json
{
  "payload": {
    "reference": {
      "id": "3b6d578e-7396-495d-8392-61b7ede174b3",
      "revision": 2
    },
    "ledgerIds": ["ethereum-mainnet"],
    "type": "v0_AddAccountLedgers"
  }
}
```

TypeScript
```typescript
const pending = await client.accounts.addLedgers(accountId, ['ethereum-mainnet']);
await pending.waitAccepted(30_000);
await pending.waitExecuted(120_000);
```

Java
```java
CoreApiAccount account = client.accounts().get(domainId, accountId);
PendingIntent pending = client.accounts().addLedgers(domainId, account, List.of("ethereum-mainnet"));
pending.waitAccepted(60);
pending.waitExecuted(120);
```

Python
```python
pending = client.accounts.add_ledgers(account_id, ["ethereum-mainnet"])
pending.wait_accepted(timeout=30)
pending.wait_executed(timeout=120)
```

For a complete program, see example `12` (account lifecycle). To get the examples, see [Examples](/pt-br/products/custody/developer-experience/sdks#examples).

## Lock or unlock an account

Use `v0_LockAccount` when an account should not be used for outbound activity. Use `v0_UnlockAccount` only after confirming the account should be re-enabled.

API reference:

```http
POST /v1/intents/dry-run
POST /v1/intents
```

The following examples lock an account:

API
```json
{
  "payload": {
    "reference": {
      "id": "3b6d578e-7396-495d-8392-61b7ede174b3",
      "revision": 2
    },
    "type": "v0_LockAccount"
  }
}
```

TypeScript
```typescript
const pending = await client.accounts.lock(accountId);
await pending.waitAccepted(30_000);
await pending.waitExecuted(120_000);

// To re-enable the account:
// await client.accounts.unlock(accountId);
```

Java
```java
CoreApiAccount account = client.accounts().get(domainId, accountId);
PendingIntent pending = client.accounts().lock(domainId, account);
pending.waitAccepted(60);
pending.waitExecuted(120);

// To re-enable the account:
// client.accounts().unlock(domainId, client.accounts().get(domainId, accountId));
```

Python
```python
pending = client.accounts.lock(account_id)
pending.wait_accepted(timeout=30)
pending.wait_executed(timeout=120)

# To re-enable the account:
# client.accounts.unlock(account_id)
```

Use `v0_UnlockAccount` with the same reference shape to re-enable the account.

## Check account balances

Before you send assets, check the amount available to send. Balances depend on transaction state and ledger-specific behavior.

API reference:

```http
GET /v1/domains/{domainId}/accounts/{accountId}/balances
```

Account balances include:

| Amount | Description |
|  --- | --- |
| Total amount | Total balance reported by the blockchain. |
| Reserved amount | Balance allocated but not available for spending, including amounts and fees for transactions that are initiated but not yet executed. |
| Quarantined amount | Incoming transfers that are not yet released from quarantine. For more information, see [Release quarantined assets](/pt-br/products/custody/transactions/send-and-receive/receive-assets-api#release-quarantined-assets). |
| Available amount | Balance available for new transactions. |


The following examples get an account's balances:

API
Call [Get account balances](/pt-br/products/custody/reference/api/openapi/accounts/getaccountbalances).

TypeScript
```typescript
import { availableAmount } from '@ripple/ripple-custody-client-lib-typescript';

const page = await client.accounts.balances(accountId, { autoPaginate: true });
const balances = Array.isArray(page) ? page : page.items;
for (const b of balances) {
  console.log(b.tickerId, b.totalAmount, b.reservedAmount, b.quarantinedAmount, availableAmount(b));
}
```

`availableAmount()` returns the spendable amount as an exact `bigint` in base units, the asset's smallest unit (for example, drops for XRP). It uses the server's available amount when the response includes one.

Java
```java
// Returns the first page of balances
CoreBalancesCollection balances = client.accounts().getBalances(domainId, accountId);
for (CoreBalance b : balances.getItems()) {
    System.out.println(b.getTickerId() + " total=" + b.getTotalAmount()
        + " available=" + b.getAvailableAmount());
}
```

Python
```python
from ripple_custody_client_lib_python import available_amount

for b in client.accounts.balances(account_id, auto_paginate=True):
    print(b.ticker_id, b.total_amount, b.reserved_amount, b.quarantined_amount, available_amount(b))
```

`available_amount()` returns the spendable amount as an exact `int` in base units, the asset's smallest unit (for example, drops for XRP). It uses the server's available amount when the response includes one.

## Force an account balance update

An account can receive funds directly from the blockchain that are not registered automatically in Ripple Custody (Vault). For example, staking rewards might not be reflected until balances are refreshed.

API reference:

```http
POST /v1/domains/{domainId}/accounts/{accountId}/balances/refresh
```

The following examples refresh an account's balances:

API
To force an account balance update, call [Update account balances forcefully](/pt-br/products/custody/reference/api/openapi/accounts/forceupdateaccountbalances).

TypeScript
```typescript
// ledgerId and tickerId are optional and limit the refresh scope
await client.accounts.refreshBalances(accountId, { ledgerId: 'ethereum-mainnet' });
```

Java
```java
client.accounts().forceUpdateAccountBalances(domainId, accountId);
```

The Java `forceUpdateAccountBalances` method sends no ledger ID, so it fails with a 400 error for accounts that have no default ledger. For those accounts, call the generated API directly: `new AccountsApi(client.getApiClient()).forceUpdateAccountBalances(domainId, accountId, ledgerId, null)`.

Python
```python
# ledger_id and ticker_id are optional and limit the refresh scope
client.accounts.refresh_balances(account_id, ledger_id="ethereum-mainnet")
```

The refresh is a direct call rather than an intent, so you don't approve it or wait on it.

## Configure compliance settings

Use account-level compliance settings to control how incoming transfers are processed. The `skipQuarantineFrom` setting allows internal transfers to bypass quarantine screening. For more information, see [Skip quarantine for internal transfers](/pt-br/products/custody/compliance/transaction-screening/concept#skip-quarantine-for-internal-transfers).

API reference:

```http
GET /v1/domains/{domainId}/accounts/{accountId}/compliance-configuration
PUT /v1/domains/{domainId}/accounts/{accountId}/compliance-configuration
GET /v1/domains/{domainId}/compliance-configurations
```

Before configuring compliance settings, prepare:

| Prerequisite | Additional information |
|  --- | --- |
| Account ID | [List accounts](/pt-br/products/custody/reference/api/openapi/accounts/getaccounts) |
| Compliance role | Contact your domain administrator. |


To retrieve the current configuration for an account, call [Get compliance configuration](/pt-br/products/custody/reference/api/openapi/accounts/getcomplianceconfiguration).

To create or update the configuration, call [Set compliance configuration](/pt-br/products/custody/reference/api/openapi/accounts/upsertcomplianceconfiguration) with the desired `skipQuarantineFrom` value and revision:

API
```json
{
  "skipQuarantineFrom": "Domain",
  "revision": 1
}
```

Java
```java
import com.ripple.custody.models.CoreComplianceConfiguration;
import com.ripple.custody.models.CoreSkipQuarantineScope;

// Read the current configuration to get its revision.
CoreComplianceConfiguration current =
    client.accounts().getComplianceConfiguration(domainId, accountId);

client.accounts().upsertComplianceConfiguration(
    domainId, accountId, CoreSkipQuarantineScope.DOMAIN, current.getMetadata().getRevision());
```

The Java methods call the API directly and don't submit an intent. To create a configuration for an account that doesn't have one, pass `null` as the revision. To list the configurations in a domain, call `listComplianceConfigurations(domainId, limit, startingAfter)`, or `streamComplianceConfigurations(domainId)` to page through them automatically.

| Value | Behavior |
|  --- | --- |
| `None` | All incoming transfers are quarantined normally. |
| `Domain` | Skip quarantine for transfers from accounts in the same domain. |
| `Instance` | Skip quarantine for transfers from any account in the same Ripple Custody (Vault) instance. |


Always retrieve the current configuration before updating so you can use the correct revision number. To disable skip quarantine, set `skipQuarantineFrom` to `None`.

To retrieve all compliance configurations in a domain, call [List compliance configurations](/pt-br/products/custody/reference/api/openapi/accounts/listcomplianceconfigurations).

The TypeScript and Python SDKs don't yet include high-level methods for account compliance configuration. Use the API operations in this section.

## Operational checklist

Before submitting an account-management intent:

- Confirm the target domain.
- Confirm the vault ID, key strategy, and ledger IDs.
- Confirm the policy that will govern the account intent.
- Dry run the payload.
- Verify the executed change by viewing account details.