> This page is for NT Connect.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://partner.ninjatrader.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://partner.ninjatrader.com/_mcp/server.

# ACH and Plaid funding

NinjaTrader Connect supports ACH deposits and withdrawals from a checking or savings account linked through Plaid. The flow creates or repairs an ACH application for a NinjaTrader trading account, then uses the approved application for funding.

> **Warning**
>
> These endpoints are Live-only. Do not send `createPlaidLinkToken`, `linkPlaidItem`, `cancelAchApplication`, or `submitFundTransaction` to the Demo base URL.

For testing, use a bearer access token for the NinjaTrader staging Live host, which is backed by Plaid Sandbox.

## Prerequisites

Your integration needs:

* a bearer access token for the user
* the user's eligible live trading account `id` and exact `name`
* Plaid Link in the user's client
* a registered `redirectUri` for a browser or iOS OAuth return flow, or a registered `androidPackageName` for an Android app-link flow
* the USD currency ID resolved for the target environment

Store durable workflow state in your own backend. A page reload, tab close, app restart, or OAuth redirect must not lose the selected mode, target, or reconciliation state.

## Complete the funding disclosure

Before an ACH deposit, the user must review and sign the **Payment Processor Disclosure and Authorization**. Follow [Funding disclosures](/connect/overview/funding/funding-disclosures), and confirm that the signature was stored before you enable the deposit action.

The disclosure applies to `ACHR` deposits. It does not gate `ACHS` withdrawals. NinjaTrader remains the source of truth and rejects a deposit with `AchDisclosureNotAccepted` when the disclosure is missing.

> **Note**
>
> Your integration must complete the disclosure signing flow and confirm it before you submit a deposit.

## Load the accounts and ACH applications

Before you create a Plaid Link token, load both prerequisite lists:

1. Call [`accountList`](/connect/api/rest-api-endpoints/accounting/account-list). Hide accounts whose `legalStatus` is `IRA`.
2. Call [`achApplicationList`](/connect/api/rest-api-endpoints/users/ach-application-list). Match applications to trading accounts by `dormanAccountId`.

Keep each eligible account's numeric `id` and exact `name`. You send them later as `dormanAccountId` and `dormanAccountName`; a name that doesn't match the account record causes `linkPlaidItem` to fail.

An account can have only one active ACH application. An application is active when it isn't archived and its status is neither `Rejected` nor `RequiresRelink`. Don't start a new link for an account that already has an active application.

## Choose the link mode

New-link and relink modes are mutually exclusive:

* **New link:** select one eligible trading account. Don't send `achApplicationId` when you create the link token.
* **Relink:** select one unarchived application whose status is `RequiresRelink`, and send its `id` as `achApplicationId`. The application determines the NinjaTrader trading account. Derive `dormanAccountId` from the application and use that account's current exact name from `accountList`; don't accept or use a separate account selection.

Changing the mode or target invalidates an existing Plaid Link token. Discard the token and create a new one before opening Plaid Link.

## Create the Plaid Link token

For a new link, call [`createPlaidLinkToken`](/connect/api/rest-api-endpoints/users/create-plaid-link-token) with an empty JSON object:

```json
{}
```

For a relink, include the selected `RequiresRelink` application's ID:

```json
{
  "achApplicationId": 12345
}
```

The platform fields are optional and mutually exclusive:

* Send `redirectUri` for a browser or iOS OAuth return flow.
* Send `androidPackageName` for an Android app-link flow.
* Send neither when the client flow doesn't use one.
* Never send both.

Register the value in NinjaTrader's Plaid configuration before using it, and omit blank optional fields. Changing either platform field invalidates an existing token.

Prevent concurrent token requests. While one request is in flight, keep the create action unavailable. If the request times out, clear the in-flight guard and require an explicit manual retry; don't start a second request automatically.

Inspect `errorText` even when the response is `HTTP 200`. Continue only when it is absent, null, or empty and the response contains a non-empty `token`. Never log, place in a URL, or display the complete token. Keep it only for the life of the link flow, in protected state that can survive an OAuth return when the client needs one.

> **Note**
>
> OAuth institutions require a return flow that reopens Plaid Link with `receivedRedirectUri`. This applies only to native mobile apps on iOS and Android. The flow requires durable state for the token. Implement and test the return flow before enabling OAuth institutions.

## Run Plaid Link

Open Plaid Link with the token. If the user exits, show Plaid's exit error and metadata, and don't call NinjaTrader.

After Plaid succeeds, select the first returned account that:

* Has type `depository`
* Has subtype `checking` or `savings`
* Doesn't have a `verification_status`

If no returned account matches, stop the flow instead of sending an incomplete request.

## Link the Plaid item

Map Plaid's `onSuccess(publicToken, metadata)` result into [`linkPlaidItem`](/connect/api/rest-api-endpoints/users/link-plaid-item):

```json
{
  "financialInstitutionName": "Example Bank",
  "accountType": "Checking",
  "accountNumber": "1234",
  "routingNumber": "ins_123456",
  "dormanAccountId": 123456,
  "dormanAccountName": "LIVE123456",
  "accountId": "plaid-account-id",
  "publicToken": "public-sandbox-example"
}
```

Use `Checking` for a Plaid `checking` subtype and `Savings` for `savings`. Use the account mask for `accountNumber`, or `"****"` when no mask is returned. Use the Plaid institution ID for `routingNumber`, or an empty string when none is returned. NinjaTrader replaces these placeholder account and routing values with the bank details it retrieves from Plaid.

Inspect `errorText` even when the response is `HTTP 200`. Continue only when it is absent, null, or empty and the response contains a numeric `achApplicationId`.

After `linkPlaidItem` succeeds, call `achApplicationList` once and find the returned ID:

* `Approved` means ACH funding can proceed.
* `UnderReview` is a valid link result, but funding must wait. Use a limited number of later checks with increasing delays; don't poll indefinitely.
* If the read fails or doesn't contain the returned ID, report the link as submitted with confirmation unavailable. Don't call `linkPlaidItem` again. Reconcile with a later read or contact NinjaTrader support.

## Submit an ACH funding request

Call [`submitFundTransaction`](/connect/api/rest-api-endpoints/funds/submit-fund-transaction) only when the selected account has an unarchived `Approved` ACH application.

Resolve `fundCurrencyId` with [`currencyList`](/connect/api/rest-api-endpoints/contract-library/currency-list). Currency IDs differ by environment. The examples below use the staging USD ID `1`; don't treat that value as an application invariant.

For a deposit, use `ACHR` and `outgoing: false`:

```json
{
  "accountId": 123456,
  "outgoing": false,
  "fundPaymentType": "ACHR",
  "fundAmount": 250,
  "fundCurrencyId": 1
}
```

For a withdrawal, use `ACHS` and `outgoing: true`:

```json
{
  "accountId": 123456,
  "outgoing": true,
  "fundPaymentType": "ACHS",
  "fundAmount": 250,
  "fundCurrencyId": 1
}
```

Send a positive amount with no more than two decimal places, except for a full-balance withdrawal. Don't add fee, bank-account, or description fields.

### Withdraw the full balance

Send an `ACHS` withdrawal with `fundAmount: 0` and `fullBalance: true`:

```json
{
  "accountId": 123456,
  "outgoing": true,
  "fundPaymentType": "ACHS",
  "fundAmount": 0,
  "fundCurrencyId": 1,
  "fullBalance": true
}
```

NinjaTrader determines the amount during processing. The request locks the trading account for immediate liquidation until it is approved or rejected. Open positions, working orders, ACH holds, pending deposits, or another pending full-balance request can cause rejection. Require an explicit confirmation that explains this effect before sending the request.

### Guard against duplicates

Keep the deposit or withdrawal action unavailable while a request is in flight. After the request might have reached NinjaTrader, don't send another one until you reconcile the transaction and account balance.

## Handle the funding response

Most business failures arrive in an `HTTP 200` response. Always inspect `errorText`, `errorHeader`, and `errorCode` before treating the operation as successful. A successful response must also contain a valid `fundTransaction` object. Display unknown status and error-code values as returned instead of assuming a fixed enumeration.

* **Known business error:** correct the reported condition before allowing another request. For example, `AchNoApprovedApplications` means the selected account doesn't have an approved ACH application.
* **Network error, `HTTP 5xx`, non-JSON body, malformed response, or `errorCode: Unknown`:** the transaction may have been created. Keep the flow locked against duplicates, and check the transaction and account balance before sending another request.
* **Valid `fundTransaction`:** show the returned ID and status. `Pending`, `InTransit`, and `Locked` are processing states, not failures.

Never retry a submitted funding request automatically.

## Cancel an ACH application

Call [`cancelAchApplication`](/connect/api/rest-api-endpoints/users/cancel-ach-application) with the application's ID from `achApplicationList`:

```json
{
  "achApplicationId": 12345
}
```

Cancellation archives the application and removes its stored Plaid connection. The API has no operation to undo it, so require an explicit confirmation before sending the request.

Business failures arrive in an `HTTP 200` response with `ok: false` and `errorText`. Treat the mutation as accepted only when the response is `{ "ok": true }`.

After an accepted cancellation, call `achApplicationList` once and find the target application:

* Confirm cancellation only when the application has `archived: true`.
* If the read fails, the application is missing, or it still has `archived: false`, report the cancellation as submitted with confirmation unavailable. Don't submit another cancellation automatically.

To use ACH funding again, start a new link after cancellation is confirmed.

## Next steps

* Keep the ACH application and fund-transaction status visible so the user can distinguish a linked bank from a fundable bank.
* Redact bearer tokens, Plaid link tokens, and Plaid public tokens from logs and user-visible errors.
* Reconcile uncertain mutations before permitting another submission.
* If you prefer a NinjaTrader-hosted flow, see [Embedding the Funding Page with an iframe](/connect/overview/partner-integration/embedding-funding-page-with-i-frame).