Closing Accounts

When a customer no longer needs an account, request its closure through the API. Closure is an asynchronous process: Modern Treasury validates the request, moves the Internal Account to pending_closure, and returns closed once the account is terminally closed.

For BYOA accounts, Modern Treasury sends the closure request to the bank. Completion therefore depends on the bank: some banks close accounts synchronously and the response may already return closed, while others leave the account in pending_closure until the bank confirms closure.

Requirements

Before requesting closure, make sure the account meets each of the following:

  • No in-flight transfers. The account must not have cancellable Payment Orders, pending originating Returns, or pending Reversals. Customer closure requests do not automatically cancel these instructions. The corresponding errors are The internal account has in-flight PaymentOrders., The internal account has in-flight Returns., and The internal account has in-flight Reversals..
  • All child accounts are closed. An account cannot be closed while any of its child accounts has a status other than closed. The error is has child accounts that are not closed.
  • The bank supports API closure. The bank must support closing accounts through the API, and the connection must have an API endpoint configured. The errors are Vendor does not support this Internal Account request. and Connection endpoint not configured.
  • The account is in a closable state. Accounts in active, suspended, or pending_activation status can begin closure. The error for an account that cannot be closed is cannot be closed.

Closure requests are supported for the following banks:

BankAccounts that can be closed via API
BNY MellonChild accounts only; parent accounts cannot be closed via the API.
Cross RiverChild accounts only; parent accounts cannot be closed via the API.
Standard CharteredChild accounts only; parent accounts cannot be closed via the API.

Requesting closure on an account at any other bank is rejected with Vendor does not support this Internal Account request.. When a bank rejects a parent-account closure, Modern Treasury returns a vendor_rejected:-prefixed error.

Modern Treasury does not validate the account balance as part of the BYOA closure request. Your bank may still reject a closure for reasons such as remaining funds; those downstream errors are surfaced with a vendor_rejected: prefix.

Request a Closure

Send a PATCH request to the Update Internal Account endpoint with status set to pending_closure.

curl --request PATCH \
     -u ORGANIZATION_ID:API_KEY \
     --url https://app.moderntreasury.com/api/internal_accounts/<internal_account_id> \
     -H 'content-type: application/json' \
     -d '{
       "status": "pending_closure"
     }'

Only pending_closure is accepted for status. Any other non-blank value is rejected with Must be 'pending_closure'., and status cannot be combined with another updatable attribute. When combined with another updatable attribute, the request is rejected with Cannot be updated while requesting closure..

A successful request returns the full Internal Account. Depending on the bank, the response status may be pending_closure or closed. The example below is abridged; see the Internal Account reference for every field.

{
  "id": "bf5f1df9-de01-40c2-9040-7ec10170a14c",
  "object": "internal_account",
  "name": "Customer Payment Account",
  "status": "pending_closure",
  "currency": "USD",
  "legal_entity_id": "8f4e2b1c-7d93-4a56-b8e1-2c9f0a3d5e67",
  "parent_account_id": null,
  "counterparty_id": null,
  "live_mode": true,
  "metadata": {},
  "created_at": "2026-01-01T16:30:00Z",
  "updated_at": "2026-02-14T09:15:00Z"
}

Closure States

StatusDescription
pending_closureThe closure request has been accepted and is being processed. The account can no longer originate payments.
closedThe account is terminally closed. It can no longer be used.

Subscribe to internal_account webhooks to be notified when the closure completes. You will receive an internal_account.closed webhook once the account reaches closed. There is no webhook for entering pending_closure or for a failed closure; closure failures are returned inline as 422 responses.

Failure Responses

Rejected closures return a 422 with the reason in errors.message and error_list[].message. For customer requests, the account reverts to its previous status, such as active or suspended; a failed request never leaves the account in pending_closure.

In-flight transfers on the account:

{
  "errors": {
    "code": "parameter_invalid",
    "message": "The internal account has in-flight PaymentOrders.",
    "parameter": "internal_account"
  },
  "error_list": [
    {
      "code": "parameter_invalid",
      "message": "The internal account has in-flight PaymentOrders.",
      "parameter": "internal_account"
    }
  ]
}

Child accounts that are not closed:

{
  "errors": {
    "code": "parameter_invalid",
    "message": "has child accounts that are not closed",
    "parameter": "internal_account"
  },
  "error_list": [
    {
      "code": "parameter_invalid",
      "message": "has child accounts that are not closed",
      "parameter": "internal_account"
    }
  ]
}

An unsupported bank:

{
  "errors": {
    "code": "parameter_invalid",
    "message": "Vendor does not support this Internal Account request.",
    "parameter": "internal_account"
  },
  "error_list": [
    {
      "code": "parameter_invalid",
      "message": "Vendor does not support this Internal Account request.",
      "parameter": "internal_account"
    }
  ]
}

A downstream bank rejection:

{
  "errors": {
    "code": "parameter_invalid",
    "message": "vendor_rejected: Account cannot be closed while funds remain",
    "parameter": "base"
  },
  "error_list": [
    {
      "code": "parameter_invalid",
      "message": "vendor_rejected: Account cannot be closed while funds remain",
      "parameter": "base"
    }
  ]
}