> 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.

# Debit-card and digital-wallet funding

NinjaTrader Connect supports deposits from an approved saved debit card and from Apple Pay or Google Pay. Both flows use Stripe in the user's client and NinjaTrader's live REST API.

> **Warning**
>
> These endpoints are Live-only. Do not send them to the Demo base URL.

## Prerequisites

Your integration needs:

* a bearer access token for the user;
* the user's eligible live `accountId`, user ID, and the USD `currencyId`;
* Stripe.js or an applicable Stripe client integration;
* for the conditional saved-card fallback, a NinjaTrader-approved Dyneti browser SDK integration and client key provisioned for your allowed origins.

Call [`getStripeConfiguration`](/connect/api/rest-api-endpoints/users/get-stripe-configuration) to obtain the Stripe publishable key. Never send Stripe secret keys to the browser.

## Complete the funding disclosures

Before either the saved-card or digital-wallet flow, the user must review and sign both funding disclosures:

1. **Form of Risk Disclosure and Customer Authorization**
2. **Payment Processor Disclosure and Authorization**

Both disclosures must be signed, but the signing order is not enforced. `addStripeDebitCard` and `startStripeFunding` reject requests when either disclosure is missing.

The disclosure-signing operation is not part of the supported Connect API contract. Direct a user who has not completed both disclosures through NinjaTrader's hosted funding experience. See [Embedding the Funding Page with an iframe](/connect/overview/partner-integration/embedding-funding-page-with-i-frame), and resume the API flow only after the user completes both disclosures.

## Check eligibility and limits

Call [`checkStripeFundingEligibility`](/connect/api/rest-api-endpoints/funds/check-stripe-funding-eligibility) before either flow:

```json
{
  "accountId": 123456,
  "currencyId": 1
}
```

Use the returned `minimumAmount` and `maximumAmount` for the amount UI. Do not hard-code them. To validate a specific deposit, repeat the request with `amount`.

Stop when `errorText` is non-empty.

## Saved debit-card flow

1. Use Stripe.js in the user's browser to collect the billing details and create a Stripe PaymentMethod.
2. Call [`addStripeDebitCard`](/connect/api/rest-api-endpoints/funds/add-stripe-debit-card) with the Stripe string PaymentMethod ID:

```json
{
  "accountId": 123456,
  "stripePaymentMethodId": "pm_example"
}
```

3. Inspect the response to choose the next step. Do not use `errorText` as the branch discriminator:

| Response                                                            | ANI result        | Next step                                                                                         |
| ------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------- |
| `{}`                                                                | Pass              | Registration is complete; call `stripeFundingMethodDependents` to resolve the approved numeric ID |
| Contains `last4`, `nameOnCard`, and numeric `stripeFundingMethodId` | Fallback required | Run the Dyneti scan and call `processDyScanResult`                                                |

### Resolve the ID after ANI approval

When `addStripeDebitCard` returns `{}`, call [`stripeFundingMethodDependents`](/connect/api/rest-api-endpoints/users/stripe-funding-method-dependents) with the user's ID:

```http
GET /v1/stripeFundingMethod/deps?masterid=654321
```

Select the new record with `status: Approved`, then use its numeric `id` as `fundingMethodId`. Do not substitute Stripe's `pm_...` identifier.

### Complete conditional Dyneti verification

The Dyneti step is not part of every saved-card registration. Run it only when `addStripeDebitCard` returns the fallback fields.

Initialize the Dyneti SDK in your client with the returned `nameOnCard`, `last4`, and `stripeFundingMethodId`. After the SDK completes, submit only its scan ID and the NinjaTrader numeric method ID:

```json
{
  "stripeFundingMethodId": 98765,
  "scanId": "9762363e-5d82-4bd3-9f7e-e0edb8be688b"
}
```

The browser-side SDK key starts the scan but is not part of this request. Do not reuse NinjaTrader dashboard credentials; contact your NinjaTrader partner representative for Dyneti enablement. NinjaTrader's funding service holds a separate server-side Dyneti API credential and uses it to retrieve the scan result directly from Dyneti.

Continue only when [`processDyScanResult`](/connect/api/rest-api-endpoints/funds/process-dy-scan-result) returns `isVerified: true` with no `errorText`.

### Start the saved-card deposit

Call [`startStripeFunding`](/connect/api/rest-api-endpoints/funds/start-stripe-funding) with NinjaTrader's approved numeric ID:

```json
{
  "accountId": 123456,
  "amount": 250,
  "currencyId": 1,
  "fundingMethodId": 98765
}
```

## Apple Pay or Google Pay flow

The wallet flow does not create a saved card and does not use ANI or Dyneti.

1. Initialize Stripe with the publishable key.
2. Use Stripe Express Checkout in the user's client to create a confirmation token.
3. Call `startStripeFunding` once for the deposit:

```json
{
  "accountId": 123456,
  "amount": 250,
  "currencyId": 1,
  "stripeConfirmationToken": "ctoken_example",
  "flowType": "WalletFunding"
}
```

The single call refers to the NinjaTrader deposit operation. Stripe client initialization, eligibility, and confirmation-token creation still happen first.

## Handle responses and rate limits

The funding endpoints return business failures in an HTTP 200 response. Always inspect `errorText`. An empty `startStripeFunding` response is not proof that the deposit succeeded: successful, canceled, and other terminal Stripe outcomes can all return an empty body.

Before calling `startStripeFunding`, retrieve the account's existing [`CashBalanceLog` entries](/connect/api/rest-api-endpoints/accounting/cash-balance-log-dependents) and record the highest `id`. After the call, poll that endpoint with the same `accountId` as `masterid`. Credit the deposit only after a newer entry appears with the requested `currencyId`, a positive `delta` equal to the deposit amount, and `cashChangeType: FundTransaction`. An empty response without that matching ledger entry is not confirmation; do not retry the charge blindly. If your integration cannot perform this reconciliation, use the [NinjaTrader-hosted funding flow](/connect/overview/partner-integration/embedding-funding-page-with-i-frame).

`addStripeDebitCard` and `startStripeFunding` are limited to 20 calls per rolling hour per IP, count every request, and use a 3-second penalty step.

Check every HTTP 200 body for `p-ticket` before interpreting it as a registration or deposit response. When `p-captcha` is absent or false, follow the [penalty-ticket procedure](/connect/overview/core-concepts/penalty-tickets).

If `p-captcha` is `true`, stop all API retries and do not retry with `p-ticket`. A server-side integration cannot solve the reCAPTCHA challenge. Direct the user to the [NinjaTrader-hosted funding flow](/connect/overview/partner-integration/embedding-funding-page-with-i-frame). If the hosted flow cannot be used, wait approximately one hour for the penalty count to clear before resuming API calls. Do not treat the penalty response as proof that a card was registered or a deposit was submitted.

## Next steps

* Use `startStripeFunding` for both approved saved cards and wallet confirmation tokens.
* Do not use `submitDebitCardFunding` as the deposit operation.
* 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).