# Finance · Wallets

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## GET /finance/wallets

**List wallets**

`operationId: PayoutController_listWallets`

Lists wallets with optional filters and paging.

#### Signature

```http
GET /finance/wallets (ownerType?: string, status?: string, page?: integer, pageSize?: integer) -> A page of wallets
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- `pageSize` is clamped to a maximum of 500 without warning.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /finance/wallets/owner/lookup`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `ownerType` | query | string | — | Filter by owner kind. |
| `status` | query | string | — |  |
| `page` | query | integer | — | Page number. Values below 1 are clamped to 1. |
| `pageSize` | query | integer | — | Page size. Clamped to 1–500; a larger value is silently reduced. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | A page of wallets |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets

**Create a wallet**

`operationId: PayoutController_createWallet`

Creates a wallet for an owner. Nothing prevents a second wallet for the same owner, so use `POST /finance/wallets/get-or-create` unless you specifically want a duplicate.

#### Signature

```http
POST /finance/wallets (body) -> The created wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- No uniqueness check on the owner — a duplicate wallet splits their balance across two records.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/get-or-create`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

Who the wallet belongs to.

```json
{
  "ownerType": "host",
  "ownerId": "cus_4821",
  "email": "ada@example.com",
  "name": "Ada Lovelace",
  "currency": "USD"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created wallet |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /finance/wallets/{walletId}

**Get a wallet**

`operationId: PayoutController_getWallet`

Fetches one wallet with its balances, holds and payout settings.

#### Signature

```http
GET /finance/wallets/{walletId} (walletId: string) -> The wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- `balance` includes held funds; `availableBalance` is what can actually be withdrawn.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /finance/wallets/transactions/lookup`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The wallet |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /finance/wallets/transactions/lookup

**Get a wallet with its transactions**

`operationId: PayoutController_getWalletWithTransactions`

Returns a wallet together with its movement history. Identify it by `walletId` or by `customerId`.

**Supplying neither returns `{ "error": "Must provide walletId or customerId" }` with a `200`**, not a `400` — check the body rather than the status.

#### Signature

```http
GET /finance/wallets/transactions/lookup (walletId?: string, customerId?: string) -> The wallet and its transactions, or an error object when no identifier was given
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Validation failure is reported in the body with a `200`, not as a `400`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /finance/wallets/{walletId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | query | string | — | Wallet id. Send this or `customerId`. |
| `customerId` | query | string | — | Customer id. Send this or `walletId`. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The wallet and its transactions, or an error object when no identifier was given |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

Example response:

```json
{
  "wallet": {
    "data": {
      "walletId": "WAL-4821",
      "balance": 1250
    }
  },
  "transactions": []
}
```

## GET /finance/wallets/owner/lookup

**Get a wallet by owner**

`operationId: PayoutController_getWalletByOwner`

Finds a wallet by who owns it, rather than by wallet id. The lookup to use when you know the host or driver but not their wallet.

#### Signature

```http
GET /finance/wallets/owner/lookup (ownerType?: string, ownerId?: string) -> The owner's wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Use `POST /finance/wallets/get-or-create` when the wallet may not exist yet.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/get-or-create`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `ownerType` | query | string | yes | Kind of owner. |
| `ownerId` | query | string | yes | Identifier within that type. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The owner's wallet |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets/get-or-create

**Get or create a wallet**

`operationId: PayoutController_getOrCreateWallet`

Returns the owner's existing wallet, creating one if they have none. **Idempotent**, which makes it the safe default: use it wherever a wallet is needed as a side effect of some other operation, rather than checking and then creating.

#### Signature

```http
POST /finance/wallets/get-or-create (body) -> The existing or newly created wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Prefer this over `POST /finance/wallets` — it cannot produce a duplicate.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

Who the wallet belongs to.

```json
{
  "ownerType": "host",
  "ownerId": "cus_4821",
  "email": "ada@example.com",
  "name": "Ada Lovelace"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The existing or newly created wallet |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets/{walletId}/credit

**Credit a wallet**

`operationId: PayoutController_creditWallet`

Adds money to a wallet — an earning, a commission, a bonus, a correction. The `type` classifies the movement for reporting, and `reference` links it to whatever generated it.

The wallet must be **active**: a frozen wallet accepts nothing, in either direction.

#### Signature

```http
POST /finance/wallets/{walletId}/credit (walletId: string, body) -> The updated wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Not idempotent — a retry credits again. Use a unique `reference` and check the history before retrying.
- Amounts here are in major units (dollars), unlike the payment endpoints which use cents.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |
| `400` | — | Wallet is not active | The wallet is frozen or otherwise not active. | Unfreeze it with `POST /finance/wallets/{walletId}/unfreeze`. A frozen wallet accepts no movement in either direction. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/{walletId}/debit`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |

### Request body

What to add, and why.

```json
{
  "amount": 250,
  "type": "earning",
  "reference": "STW-4821",
  "description": "Host earnings for booking STW-4821"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated wallet |
| `400` | Wallet is not active — The wallet is frozen or otherwise not active. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets/{walletId}/debit

**Debit a wallet**

`operationId: PayoutController_debitWallet`

Takes money out of a wallet — a fee, a clawback, a correction. Refused when the balance is insufficient, so a wallet cannot go negative through this route.

#### Signature

```http
POST /finance/wallets/{walletId}/debit (walletId: string, body) -> The updated wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Debits are checked against `balance`, not `availableBalance` — held funds are not protected from a debit.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |
| `400` | — | Wallet is not active | The wallet is frozen or otherwise not active. | Unfreeze it with `POST /finance/wallets/{walletId}/unfreeze`. A frozen wallet accepts no movement in either direction. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/{walletId}/credit`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |

### Request body

What to remove, and why.

```json
{
  "amount": 50,
  "type": "fee",
  "reference": "INV-4821",
  "description": "Platform fee"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated wallet |
| `400` | Wallet is not active — The wallet is frozen or otherwise not active. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets/{walletId}/hold

**Hold funds in a wallet**

`operationId: PayoutController_holdFunds`

Makes part of a balance unavailable without removing it — for a dispute, a chargeback, a security review or a deposit. The money stays in `balance` but comes out of `availableBalance`, so it cannot be paid out while the hold stands.

Set `expiresAt` for a hold that should lapse on its own.

#### Signature

```http
POST /finance/wallets/{walletId}/hold (walletId: string, body) -> The updated wallet, with the hold appended
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Holds are stored positionally, and released by index — read the wallet to find the right one before releasing.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |
| `400` | INSUFFICIENT_BALANCE | Insufficient balance for hold | The hold exceeds what is available. | Check `availableBalance` before holding. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/{walletId}/release/{holdIndex}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |

### Request body

What to hold, and why.

```json
{
  "reason": "dispute",
  "amount": 250,
  "reference": "RMA-4821",
  "notes": "Customer disputed the booking"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated wallet, with the hold appended |
| `400` | Insufficient balance for hold — The hold exceeds what is available. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets/{walletId}/release/{holdIndex}

**Release held funds**

`operationId: PayoutController_releaseFunds`

Removes a hold and returns the amount to `availableBalance`.

**The hold is identified by its position in the `holds` array, not by an id.** Read the wallet immediately before releasing and use the current index: releasing one hold shifts the positions of those after it, so a stale index releases the wrong hold.

#### Signature

```http
POST /finance/wallets/{walletId}/release/{holdIndex} (walletId: string, holdIndex: integer) -> The updated wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Positional identification makes this unsafe to run concurrently against one wallet — two simultaneous releases can target the wrong holds.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/{walletId}/hold`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |
| `holdIndex` | path | integer | yes | Zero-based index into the wallet's `holds` array. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated wallet |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets/{walletId}/freeze

**Freeze a wallet**

`operationId: PayoutController_freezeWallet`

Suspends a wallet completely. While frozen it accepts no credits, no debits and no payouts — the heavier-handed alternative to holding a specific amount.

Set `unfreezeAt` for a freeze that should lift on its own.

#### Signature

```http
POST /finance/wallets/{walletId}/freeze (walletId: string, body) -> The frozen wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Notes

- Freezing blocks credits too — earnings cannot be recorded while the wallet is frozen. Prefer a hold when only outgoing money should stop.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/{walletId}/unfreeze`
- `POST /finance/wallets/{walletId}/hold`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |

### Request body

Why the wallet is being frozen.

```json
{
  "reason": "Suspected fraudulent activity",
  "frozenBy": "risk@appmint.io"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The frozen wallet |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /finance/wallets/{walletId}/unfreeze

**Unfreeze a wallet**

`operationId: PayoutController_unfreezeWallet`

Returns a frozen wallet to active. Only a frozen wallet can be unfrozen — calling this on an active one is an error rather than a no-op.

#### Signature

```http
POST /finance/wallets/{walletId}/unfreeze (walletId: string) -> The reactivated wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |
| `400` | — | Wallet is not frozen | The wallet is already active. | Read the wallet status first — this is not idempotent. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/{walletId}/freeze`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The reactivated wallet |
| `400` | Wallet is not frozen — The wallet is already active. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PUT /finance/wallets/{walletId}/payout-settings

**Update payout settings**

`operationId: PayoutController_updatePayoutSettings`

Sets the limits and destinations for a wallet's payouts — minimum, maximum, daily cap, and the payout methods available.

These are exactly the rules `request-payout` enforces, so a payout rejected for being below the minimum is governed here.

#### Signature

```http
PUT /finance/wallets/{walletId}/payout-settings (walletId: string, body) -> The updated wallet
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | — | Wallet not found | No wallet in the org has that id. | List wallets with `GET /finance/wallets`, or look one up by owner. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /finance/wallets/{walletId}/request-payout`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `walletId` | path | string | yes | Wallet id. |

### Request body

The settings to apply.

```json
{
  "minPayout": 25,
  "maxPayout": 5000,
  "dailyLimit": 10000
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The updated wallet |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Wallet not found — No wallet in the org has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /finance/stats/wallets

**Get wallet statistics**

`operationId: PayoutController_getWalletStats`

Aggregate wallet figures for the org — how many wallets exist and how much is held across them. The outstanding-liability view.

#### Signature

```http
GET /finance/stats/wallets () -> Aggregate wallet statistics
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `Staff — a signed-in business user; customer and site-app tokens are refused`.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /finance/stats/payouts`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Aggregate wallet statistics |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

