---
title: "Transfer funds between two tenants within the same omnibus"
method: POST
path: "/v1/domains/{domainId}/omnibus/{omnibusId}/tenants/{tenantId}/internal-transfers"
tags: ["Internal transfers"]
---

# Transfer funds between two tenants within the same omnibus

`POST /v1/domains/{domainId}/omnibus/{omnibusId}/tenants/{tenantId}/internal-transfers`

## Request

- Base URL: `https://docs.ripple.com` (the document declares no server; this is the origin it was published from)
- URL: `https://docs.ripple.com/v1/domains/{domainId}/omnibus/{omnibusId}/tenants/{tenantId}/internal-transfers`
- Auth: none declared. Required headers: `Idempotency-Key`

Moves an amount from a source tenant to a destination tenant within the same omnibus by recording a cross-account entry in the Accounting service. Asynchronous: accepts the request, durably persists the operation, starts a Temporal workflow (validate → reserve → post to Accounting → finalize), and returns 202 with status=PENDING immediately. Poll `GET .../internal-transfers/{operationId}` for the terminal status. A required, per-transfer Idempotency-Key makes retries safe — reuse the same key to retry a transfer, use a new key for a new transfer. See `InternalTransferStatus` for the meaning of each status.

## Path parameters

- `domainId` string, uuid, required
- `omnibusId` string, uuid, required
- `tenantId` string, uuid, required

## Headers

- `Idempotency-Key` string, required

## Request body

- OmnibusCreateInternalTransferRequest
  - `destTenantId` string, uuid, required: Destination tenant ID (funds are credited to this tenant)
  - `amount` string, required: Positive integer in the ticker's smallest unit (e.g., wei for ETH, satoshis for BTC, drops for XRP). No leading zeros; zero is not allowed.
  - `tickerId` string, uuid, required: Identifier of the ticker to transfer
  - `ledgerId` string, required: Identifier of the blockchain ledger; must match on both source and destination

## Response `202`

Transfer accepted and being processed. Body carries status=PENDING and the operationId; poll `GET .../internal-transfers/{operationId}` for the terminal status.

- OmnibusInternalTransferResponse
  - `operationId` string, uuid, required: Operation ID for this internal transfer
  - `transferId` string, uuid, required: Transfer ID for this movement
  - `status` 'PENDING' | 'COMPLETED' | 'FAILED' | 'PENDING_VERIFICATION', required
  - `sourceTenantId` string, uuid, required: Source tenant (debited)
  - `destTenantId` string, uuid, required: Destination tenant (credited)
  - `amount` string, required: Positive integer in the ticker's smallest unit
  - `tickerId` string, uuid, required
  - `ledgerId` string, required

## Other responses

- `400`: Invalid request (e.g. source equals destination, malformed body, missing or blank Idempotency-Key)
- `404`: Omnibus or source tenant not found
- `409`: Omnibus is not operational (ACTIVE and UNLOCKED) or a tenant is not UNLOCKED, insufficient available balance, or concurrent reservation conflict or Idempotency-Key reused with a different payload (same key + source, different transfer details)
- `500`: Unexpected server error (e.g. database failure)

---

[API](https://skmtc.dev/ripple/apis/ripple-custody-api.md) - [All operations](https://skmtc.dev/ripple/apis/ripple-custody-api/llms.txt) - [OpenAPI document](https://skmtc.dev/ripple/apis/ripple-custody-api/revisions/c6f0ad6b70c0?raw)
