> ## Documentation Index
> Fetch the complete documentation index at: https://docs.centryos.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdraw to Linked Account

> Initiate a payout from an end-user's SPEND wallet to a linked payout method (bank or card).

## Overview

Initiate a payout from an end-user's **SPEND** wallet to a linked payout method (bank account or card). The wallet is inferred from the linked account; the caller does not pass a wallet ID.

**Auth:** Bearer token with scope `external.apis` (authorizer: `walletos-account-service-{stage}-authorizer`).

**Rate limit:** 1 withdrawal per minute per user (and IP). Returns 429 if exceeded.

## How it works

1. **Auth** – Request must include a valid Bearer token; `claims.sub` is the entity (user/business) making the withdrawal.
2. **Linked account** – `linkedAccountId` must exist and its wallet must be associated with that entity.
3. **Validation** – Body is validated (amount, currency, walletType, reason; **MFA is not required** for this external endpoint).
4. **Balance** – Wallet balance must be at least `amount + fees`.
5. **Payout** – Debit wallet, create transaction, and process payout. If the linked account is a **bank** and `routingType` is **RTP**, real-time payment (RTP) is used; otherwise ACH is used. For **card** linked accounts, push-to-card is used regardless of `routingType`.

**Important:** The linked account must have been created first (e.g. via the link-account flow). Supported payout methods include ACH, RTP, WIRE, and card.

## Endpoint

* **POST** `{{LIQUIDITY_URL}}/v1/ext/linked-accounts/:currency/:linkedAccountId/withdrawal`
* Auth: Bearer `{{accessToken}}` (scope `external.apis`)

### Path Parameters

| Parameter         | Type   | Required | Description                                                                                                                                                                                                                                                                                                                                                                                   |
| ----------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `currency`        | string | Yes      | Currency code. Supported: `USD` only.                                                                                                                                                                                                                                                                                                                                                         |
| `linkedAccountId` | string | Yes      | UUID of the linked payout account (bank or card) to send funds to. Must be a valid UUID of an existing linked account whose wallet is associated with the authenticated entity. See [Link Bank Account](/guides/link-bank-account), [Link Card Account](/guides/link-card-account), or [Link International Bank Account](/guides/link-international-bank-account) for how to link an account. |

### Headers

| Header          | Required | Description                                  |
| --------------- | -------- | -------------------------------------------- |
| `Authorization` | Yes      | `Bearer <token>` with scope `external.apis`. |
| `Content-Type`  | Yes      | `application/json`.                          |

## Request Body

| Field             | Type   | Required | Description                                                                                                                                                                                                     |
| ----------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `amount`          | number | Yes      | Payout amount (e.g. dollars for USD). Must be > 0.                                                                                                                                                              |
| `walletType`      | string | Yes      | Must be `"SPEND"`. Withdrawal is from the SPEND wallet tied to the linked account.                                                                                                                              |
| `reason`          | string | Yes      | Reason or reference for the payout (e.g. "Contractor payment", "Payout").                                                                                                                                       |
| `routingType`     | string | No       | **Allowed values only:** `ACH`, `RTP`, `WIRE`, `BANK`. Used for **bank** linked accounts: `RTP` = real-time payment; `ACH` or `BANK` = ACH; `WIRE` = wire. Ignored for **card** linked accounts (push-to-card). |
| `transactionType` | string | No       | If `"payout"` (case-insensitive), treated as PAYMENT; otherwise flow is PAYOUT.                                                                                                                                 |

**Note:** **MFA is not required** for this external payout endpoint. Do not send `mfaCode`.

## Request Examples

### RTP bank payout

```bash theme={null}
curl -X POST "{{LIQUIDITY_URL}}/v1/ext/linked-accounts/USD/{{linkedAccountId}}/withdrawal" \
  -H "Authorization: Bearer {{accessToken}}" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100.50,
    "walletType": "SPEND",
    "reason": "Contractor payment",
    "routingType": "RTP"
  }'
```

### ACH bank payout

```json theme={null}
{
  "amount": 250.00,
  "walletType": "SPEND",
  "reason": "Contractor payment",
  "routingType": "ACH"
}
```

### Card payout (routingType optional)

```json theme={null}
{
  "amount": 50.00,
  "walletType": "SPEND",
  "reason": "Refund"
}
```

## Supported Payout Types

| Linked account type | `routingType` (optional)   | Result                                  |
| ------------------- | -------------------------- | --------------------------------------- |
| **BANK**            | `RTP`                      | Real-time payment (RTP) to bank.        |
| **BANK**            | `ACH` or `BANK` or omitted | ACH transfer to bank.                   |
| **BANK**            | `WIRE`                     | Wire-style handling (method-dependent). |
| **CARD**            | any or omitted             | Push-to-card. `routingType` is ignored. |

**Allowed `routingType` values:** `ACH`, `RTP`, `WIRE`, `BANK` only. Any other value returns a validation error.

## Response

### Success (200)

```json theme={null}
{
  "success": true,
  "message": "Payment queued",
  "data": {
    "id": "<transaction-uuid>",
    "status": "<TransactionStatus>",
    "fees": "<fee amount as string>",
    "amount": "<total debit amount (amount + fees) as string>"
  }
}
```

* **Queued** means the withdrawal was accepted and processing started. Final status may be delivered via webhooks or a separate status API.
* `data.amount` is the total debited from the wallet (payout amount + fees).

### Response Fields

| Field         | Type    | Description                                   |
| ------------- | ------- | --------------------------------------------- |
| `success`     | boolean | Whether the request succeeded.                |
| `message`     | string  | Status message (e.g. "Payment queued").       |
| `data.id`     | string  | Transaction UUID for tracking.                |
| `data.status` | string  | Transaction status (e.g. SUCCESS, PENDING).   |
| `data.fees`   | string  | Fee amount as string.                         |
| `data.amount` | string  | Total debit amount (amount + fees) as string. |

### Error (4xx / 5xx)

```json theme={null}
{
  "success": false,
  "message": "<error detail>",
  "fatal": true
}
```

### Typical Error Cases

| Situation                      | HTTP | Message / cause                                                 |
| ------------------------------ | ---- | --------------------------------------------------------------- |
| Invalid or missing body        | 400  | Validation errors (e.g. missing amount, walletType, reason).    |
| Invalid routingType            | 400  | Only `ACH`, `RTP`, `WIRE`, `BANK` are allowed.                  |
| Unauthorized / wrong scope     | 401  | Invalid or expired token, or missing `external.apis` scope.     |
| Linked account not found       | 404  | Wrong ID or linked account not accessible.                      |
| Application token expired      | 400  | When using a payment-link token that is expired/inactive.       |
| Amount mismatch (payment link) | 400  | When withdrawal is merchant-funded and amount ≠ token amount.   |
| Insufficient balance           | 400  | `"Insufficient Funds #912"`.                                    |
| Rate limit                     | 429  | 1 withdrawal per minute per user.                               |
| Server / processing error      | 500  | `"Something went wrong, please try again"` or processing error. |

## Validation Rules (Summary)

* **amount** – Required, number, > 0.
* **walletType** – Required, must be `"SPEND"`.
* **reason** – Required, string.
* **routingType** – Optional. If present, must be one of: `ACH`, `RTP`, `WIRE`, `BANK`.
* **currency** – From path; must be `USD` (only supported currency).
* **linkedAccountId** – From path; must be a valid UUID of an existing linked account whose wallet is associated with the authenticated entity. See [Link Bank Account](/guides/link-bank-account), [Link Card Account](/guides/link-card-account), or [Link International Bank Account](/guides/link-international-bank-account) for how to link an account.
* **MFA** – **Not required** for this external endpoint. Do not send `mfaCode`.

## Payment-Link / Merchant-Funded Withdrawals

If the request is made in the context of a **payment link** (application token) with `withdrawalSource === 'MERCHANT_WALLET'`:

* **amount** in the body must equal the amount on the application token; otherwise the response is 400 `"Malformed request"`.
* On success, the application token is invalidated immediately.

## Notes for External Engineers

1. **Linked account first** – Ensure the payout destination is already linked (e.g. via the link-account widget/API) and that you have the correct `linkedAccountId` for the entity's SPEND wallet.
2. **No MFA** – This external payout endpoint does **not** require MFA. Omit `mfaCode` from the request.
3. **RTP for faster bank payouts** – For bank linked accounts, send `routingType: "RTP"` to use real-time payment when supported.
4. **Fees** – Fees depend on payout method and configuration. The response `data.fees` and `data.amount` reflect the fee and total debit.
5. **Async outcome** – Success means "queued"; final success/failure may be delivered asynchronously (e.g. webhooks). Use your transaction/withdrawal status APIs or webhooks to track final state.
6. **Rate limit** – 1 request per minute per user (and IP). Design retries and UX accordingly.
7. **Idempotency** – The API does not expose idempotency keys. Retrying the same request can create multiple withdrawals. Implement your own idempotency (e.g. linkedAccountId + amount + reason + timestamp) if you need to avoid duplicates.
