O Auth Token

View as Markdown
### Exchange a credential for an access token. **Available to:** Registered OAuth client applications and NT Connect partners **Environments:** Demo, Live. The `jwt-bearer` grant is Live in practice, because partner configurations are provisioned there. **[Rate Limit](/overview/core-concepts/rate-limits):** 10 requests per hour, 30-second back-off, counts failed requests only. OAuth errors are returned as `HTTP 200` and are not failures for this purpose, so only transport-level failures count toward the penalty. Select a grant with `grant_type`; which of the other request fields apply depends on the grant you choose. | `grant_type` | Use it to | Required fields | |---|---|---| | `authorization_code` | Redeem a code from the authorize endpoint | `code`, `redirect_uri` | | `refresh_token` | Renew an access token without re-authenticating | `refresh_token` | | `urn:ietf:params:oauth:grant-type:jwt-bearer` | Exchange a signed partner assertion for a token on behalf of one of your users | `assertion` | | `client_credentials` | Authenticate a headless client that acts as its own service account, with no user present. The response carries no refresh token; re-authenticate when the access token expires. | `client_id`, `client_secret` | Authenticate the client with `client_id` and `client_secret` in the body, or with `httpAuth` (base64 `client_id:client_secret`). Public clients use PKCE and send `code_verifier` instead of a secret. For the authorization-code flow, see the [OAuth JavaScript tutorial](https://github.com/tradovate/example-api-oauth). <Warning>**Warning:** This endpoint returns **HTTP 200** for successes **and** errors. Always check for an `error` field in the response body to determine whether the request succeeded or failed.</Warning> **Partner SSO: the `jwt-bearer` Grant** NT Connect partners exchange a short-lived JWT, signed with their registered key, for a NinjaTrader access token scoped to one of their users. No user password is involved and no NinjaTrader login prompt is shown. **Assertion Requirements** The assertion must be signed with **RS256** and carry a `kid` header identifying the signing key. Required claims: | Claim | Value | |---|---| | `iss` | The issuer registered on your partner configuration. It must match exactly. | | `sub` | The numeric NinjaTrader user ID of the user you are acting for. Users are never auto-provisioned; the account must already exist and belong to your organization. | | `aud` | The audience registered on your partner configuration (in practice the token endpoint URL of the environment you are calling). It must match exactly. | | `iat` | Issued-at time. | | `exp` | Expiry. `exp` minus `iat` must not exceed **300 seconds**; 60 seconds is recommended. | | `jti` | A unique identifier. Reusing one is rejected as a replay. | Clock skew of up to 60 seconds is tolerated on the time claims. Any additional claims you include are ignored. **Signing Keys** NinjaTrader fetches your public keys from the JWKS URL on your partner configuration and caches them for one hour. A key rotation is picked up automatically the first time an assertion arrives with a `kid` that is not in the cache, so publish the new key before you start signing with it. **Sample Call** ```bash curl -X POST 'https://live.tradovateapi.com/v1/auth/oauthtoken' \ -H 'Content-Type: application/json' \ -d '{ "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": "<your-signed-jwt>" }' ``` ```json { "access_token": "…", "refresh_token": "…", "token_type": "bearer", "expires_in": 86400, "refresh_token_expires_in": 93600 } ``` <Info>Read the token lifetimes from `expires_in` and `refresh_token_expires_in` rather than hard-coding them. They are environment configuration, and there is no partner-specific override. An `id_token` is only issued on the `authorization_code` grant; it is always null for `jwt-bearer`.</Info> **Scopes** The resulting token carries the scopes configured on your partner client application, intersected with the scopes the user has granted. A user who has revoked your application is rejected with `access_denied`. **Refresh Token Behavior** Refresh tokens are single-use and rotate: each refresh consumes the current token and returns a new one. Because a partner session is keyed on the partner and the user rather than on a device, **a new `jwt-bearer` exchange replaces the refresh token issued by the previous exchange for that same user.** Keep the most recent token pair, or re-run the exchange, rather than holding several in parallel. **Signing a User Into a NinjaTrader-Hosted Page** To hand a signed-in user off to a NinjaTrader-hosted page, use the partner access token from this endpoint to mint a short-lived code with [`shortGrantCode`](/api/rest-api-endpoints/authentication/short-grant-code), then redirect the user's browser with that code attached. The access token itself never leaves your backend. The NinjaTrader application redeems the code with [`exchangeShortGrantCode`](/api/rest-api-endpoints/authentication/exchange-short-grant-code). **Common Failure Scenarios** - The assertion is signed with an algorithm other than RS256, or omits the `kid` header. - `iss` or `aud` does not exactly match the values registered on the partner configuration. - The assertion lifetime exceeds 300 seconds. - The same `jti` was already used. - `sub` does not resolve to an active user in your organization. - The partner configuration is disabled or archived. - `assertion` is missing on a `jwt-bearer` call, which matches no grant shape and returns `bad_request`. **Error Messages** | `error` | `error_description` | Trigger | |---------|---------------------|---------| | `invalid_client` | `Unknown issuer: <iss>` | No partner configuration matches the assertion's `iss` | | `invalid_client` | `Provider is disabled: <partner>` / `Provider is archived: <partner>` | The partner configuration is not currently usable | | `invalid_client` | `Partner SSO client application is not configured` | The partner's client application is missing or not authorized for this flow | | `invalid_grant` | `Invalid assertion: …` | Expired assertion, bad signature, audience mismatch, a missing `kid`, `sub`, `iat`, `exp`, or `jti`, or a lifetime over the 300-second cap | | `invalid_grant` | `Assertion jti has already been used` | Replayed assertion | | `invalid_grant` | `User not found: userId=<id>` | `sub` is a valid user ID but no such user exists. This is the usual shape. | | `invalid_grant` | `User not found: sub=<sub>` | `sub` is not a positive integer, so it never resolved to a user ID | | `invalid_grant` | `User account is not active: <userId>` | The user account is inactive | | `invalid_grant` | The user is not in your partner organization | The user belongs to a different organization. The message also reports the organization IDs involved; treat that detail as diagnostic rather than a stable format. | | `access_denied` | `User has denied this application` | The user revoked your application | | `unsupported_grant_type` | `Supported: authorization_code, refresh_token, client_credentials, urn:ietf:params:oauth:grant-type:jwt-bearer` | An unrecognized `grant_type` from a public client. Confidential clients get `bad_request` instead. | | `bad_request` | `Invalid request` | The request shape matched no supported grant, including an unrecognized `grant_type` sent with a client secret | | `server_error` | `Internal error: …` | Unexpected server-side failure. The detail relays the underlying error text; do not parse it. |

Request

This endpoint expects an object.
grant_typestringRequired<=64 characters
codestringOptional<=8192 characters
redirect_uristringOptional<=8192 characters
client_idstringOptional<=8192 characters
client_secretstringOptional<=8192 characters
httpAuthstringOptional<=8192 characters
refresh_tokenstringOptional<=8192 characters
code_verifierstringOptional<=8192 characters
resourcestringOptional<=8192 characters
assertionstringOptional<=8192 characters

Response

OAuthTokenResponse
access_tokenstringOptional<=8192 characters
refresh_tokenstringOptional<=8192 characters
token_typestringOptional<=64 characters
expires_inintegerOptional
refresh_token_expires_inintegerOptional
errorstringOptional<=64 characters
error_descriptionstringOptional<=8192 characters
id_tokenstringOptional<=8192 characters