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

# Short Grant Code

POST https://live.tradovateapi.com/v1/auth/shortgrantcode
Content-Type: application/json

### Mint a single-use code that signs one of your users into a NinjaTrader-hosted page.

**Available to:** Trader users of a configured NT Connect partner organization

**Environments:** Live. The endpoint is not blocked on Demo, but partner mode depends on a partner configuration, and those are provisioned on Live.

**[Rate Limit](/overview/core-concepts/rate-limits):** No endpoint-specific time penalty. Partner-mode requests are limited per partner organization: 1,000 requests per hour by default, measured over a sliding window.

Use this endpoint when a user is already signed in to your application and you want to send their browser to a NinjaTrader-hosted URL (the trader dashboard, an account page, a funding page) without a second login.

Your backend calls this endpoint with the partner access token it already holds for that user (minted with the `urn:ietf:params:oauth:grant-type:jwt-bearer` grant on [`oAuthToken`](/api/rest-api-endpoints/authentication/o-auth-token)). NinjaTrader returns a short-lived, single-use code. You then redirect the user's browser to the NinjaTrader application with that code attached, and [`exchangeShortGrantCode`](/api/rest-api-endpoints/authentication/exchange-short-grant-code) redeems it on arrival.

**The partner access token never leaves your backend.** Only the short code travels through the browser.

The handoff runs in three steps:

1. Your backend posts to `/auth/shortgrantcode` with the partner access token.
2. NinjaTrader returns `code` and `expires_in`.
3. Your backend redirects the user's browser to the NinjaTrader application with the code attached, and the application redeems it.

<Warning>**Warning:** This endpoint returns **HTTP 200** for successes **and** for policy rejections. Always check the `errorText` field in the response body to determine whether the request succeeded or failed.</Warning>

**Partner Mode and `expectedClientIp`**

`expectedClientIp` is what selects partner mode, and it is the field that makes the flow work end to end. Send the end user's IP address as resolved by your own load-balancer-aware logic. The code is then bound to that address and redemption is rejected from anywhere else.

<Info>Omitting `expectedClientIp` does not return an error. The request silently falls back to the non-partner path, which binds the code to **your backend's** IP address instead of the user's, so the redirect fails at redemption with `"Access denied from another IP"`. Always send it.</Info>

In partner mode the caller must be a trader user belonging to an organization that has an enabled, non-archived partner configuration. Organization administrators and non-trader accounts are rejected.

**Code Lifetime**

`ttl` is a request, not a guarantee. The server clamps it to the per-environment maximum (**30 seconds in production**), and the response's `expires_in` carries the value actually stored. Omitting `ttl`, or sending a value of zero or less, gives 15 seconds.

Mint the code at the moment you are ready to redirect, not in advance. Only one code exists per user at a time: minting a second code invalidates the first.

**Response Fields**

The response is an `OAuthGrantResponse`. Two fields matter for this flow:

- `code`: the single-use code. Attach it to the NinjaTrader URL you redirect the user to.
- `expires_in`: the code's lifetime in seconds, after clamping. Note the underscore: the JSON key is `expires_in`, not `expiresIn`.

The `idToken` field is never populated by this endpoint.

**Sample Call**

```bash
CODE=$(curl -sS -X POST "https://live.tradovateapi.com/v1/auth/shortgrantcode" \
  -H "Authorization: Bearer $PARTNER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"ttl\": 30, \"expectedClientIp\": \"$END_USER_IP\"}" \
  | jq -r .code)

# Redirect the user's browser to the NinjaTrader application with the code attached.
```

```json
{
  "code": "…",
  "expires_in": 30
}
```

**Common Failure Scenarios**

- `expectedClientIp` is omitted, so the code is bound to your backend's address instead of the user's.
- `expectedClientIp` is not a valid IPv4 or IPv6 address, or exceeds 64 characters. Both are request-validation failures, so they return `HTTP 400` with a field-violation body rather than the `HTTP 200` shape below.
- The caller's organization has no partner configuration, or that configuration is disabled or archived.
- The caller is an organization administrator or a non-trader account.
- The access token is missing or expired (returns `HTTP 401`, plain-text body).
- The organization exceeded its hourly quota (returns `HTTP 429`, plain-text body).
- The code was minted too early and expired before the user's browser reached NinjaTrader.

**Error Messages**

| Response | Trigger |
|----------|---------|
| `errorText: "Short grant codes are not available for this account"` | Any partner-policy rejection: no partner configuration, configuration disabled or archived, caller is an organization administrator, or caller is not a trader. The specific reason is deliberately not disclosed. |
| `HTTP 429` `Partner short grant code rate limit exceeded` | The organization exceeded its hourly quota. The body is plain text, not JSON. |
| `HTTP 401` | The access token is missing, invalid, or expired. The body is plain text, not JSON. |

Reference: https://partner.ninjatrader.com/connect/api/rest-api-endpoints/authentication/short-grant-code

## OpenAPI Specification

```yaml
openapi: 3.1.0
info:
  title: connect
  version: 1.0.0
paths:
  /auth/shortgrantcode:
    post:
      operationId: short-grant-code
      summary: Short Grant Code
      description: >-
        ### Mint a single-use code that signs one of your users into a
        NinjaTrader-hosted page.


        **Available to:** Trader users of a configured NT Connect partner
        organization


        **Environments:** Live. The endpoint is not blocked on Demo, but partner
        mode depends on a partner configuration, and those are provisioned on
        Live.


        **[Rate Limit](/overview/core-concepts/rate-limits):** No
        endpoint-specific time penalty. Partner-mode requests are limited per
        partner organization: 1,000 requests per hour by default, measured over
        a sliding window.


        Use this endpoint when a user is already signed in to your application
        and you want to send their browser to a NinjaTrader-hosted URL (the
        trader dashboard, an account page, a funding page) without a second
        login.


        Your backend calls this endpoint with the partner access token it
        already holds for that user (minted with the
        `urn:ietf:params:oauth:grant-type:jwt-bearer` grant on
        [`oAuthToken`](/api/rest-api-endpoints/authentication/o-auth-token)).
        NinjaTrader returns a short-lived, single-use code. You then redirect
        the user's browser to the NinjaTrader application with that code
        attached, and
        [`exchangeShortGrantCode`](/api/rest-api-endpoints/authentication/exchange-short-grant-code)
        redeems it on arrival.


        **The partner access token never leaves your backend.** Only the short
        code travels through the browser.


        The handoff runs in three steps:


        1. Your backend posts to `/auth/shortgrantcode` with the partner access
        token.

        2. NinjaTrader returns `code` and `expires_in`.

        3. Your backend redirects the user's browser to the NinjaTrader
        application with the code attached, and the application redeems it.


        <Warning>**Warning:** This endpoint returns **HTTP 200** for successes
        **and** for policy rejections. Always check the `errorText` field in the
        response body to determine whether the request succeeded or
        failed.</Warning>


        **Partner Mode and `expectedClientIp`**


        `expectedClientIp` is what selects partner mode, and it is the field
        that makes the flow work end to end. Send the end user's IP address as
        resolved by your own load-balancer-aware logic. The code is then bound
        to that address and redemption is rejected from anywhere else.


        <Info>Omitting `expectedClientIp` does not return an error. The request
        silently falls back to the non-partner path, which binds the code to
        **your backend's** IP address instead of the user's, so the redirect
        fails at redemption with `"Access denied from another IP"`. Always send
        it.</Info>


        In partner mode the caller must be a trader user belonging to an
        organization that has an enabled, non-archived partner configuration.
        Organization administrators and non-trader accounts are rejected.


        **Code Lifetime**


        `ttl` is a request, not a guarantee. The server clamps it to the
        per-environment maximum (**30 seconds in production**), and the
        response's `expires_in` carries the value actually stored. Omitting
        `ttl`, or sending a value of zero or less, gives 15 seconds.


        Mint the code at the moment you are ready to redirect, not in advance.
        Only one code exists per user at a time: minting a second code
        invalidates the first.


        **Response Fields**


        The response is an `OAuthGrantResponse`. Two fields matter for this
        flow:


        - `code`: the single-use code. Attach it to the NinjaTrader URL you
        redirect the user to.

        - `expires_in`: the code's lifetime in seconds, after clamping. Note the
        underscore: the JSON key is `expires_in`, not `expiresIn`.


        The `idToken` field is never populated by this endpoint.


        **Sample Call**


        ```bash

        CODE=$(curl -sS -X POST
        "https://live.tradovateapi.com/v1/auth/shortgrantcode" \
          -H "Authorization: Bearer $PARTNER_ACCESS_TOKEN" \
          -H "Content-Type: application/json" \
          -d "{\"ttl\": 30, \"expectedClientIp\": \"$END_USER_IP\"}" \
          | jq -r .code)

        # Redirect the user's browser to the NinjaTrader application with the
        code attached.

        ```


        ```json

        {
          "code": "…",
          "expires_in": 30
        }

        ```


        **Common Failure Scenarios**


        - `expectedClientIp` is omitted, so the code is bound to your backend's
        address instead of the user's.

        - `expectedClientIp` is not a valid IPv4 or IPv6 address, or exceeds 64
        characters. Both are request-validation failures, so they return `HTTP
        400` with a field-violation body rather than the `HTTP 200` shape below.

        - The caller's organization has no partner configuration, or that
        configuration is disabled or archived.

        - The caller is an organization administrator or a non-trader account.

        - The access token is missing or expired (returns `HTTP 401`, plain-text
        body).

        - The organization exceeded its hourly quota (returns `HTTP 429`,
        plain-text body).

        - The code was minted too early and expired before the user's browser
        reached NinjaTrader.


        **Error Messages**


        | Response | Trigger |

        |----------|---------|

        | `errorText: "Short grant codes are not available for this account"` |
        Any partner-policy rejection: no partner configuration, configuration
        disabled or archived, caller is an organization administrator, or caller
        is not a trader. The specific reason is deliberately not disclosed. |

        | `HTTP 429` `Partner short grant code rate limit exceeded` | The
        organization exceeded its hourly quota. The body is plain text, not
        JSON. |

        | `HTTP 401` | The access token is missing, invalid, or expired. The
        body is plain text, not JSON. |
      tags:
        - Authentication
      parameters:
        - name: Authorization
          in: header
          description: Bearer authentication
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OAuthGrantResponse
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthGrantResponse'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShortGrantCode'
servers:
  - url: https://live.tradovateapi.com/v1
    description: Live
components:
  schemas:
    ShortGrantCode:
      type: object
      properties:
        ttl:
          type: integer
        expectedClientIp:
          type: string
      title: ShortGrantCode
    OAuthGrantResponse:
      type: object
      properties:
        errorText:
          type: string
          description: Non-empty if the request failed
        code:
          type: string
        expires_in:
          type: integer
        idToken:
          type: string
      title: OAuthGrantResponse
  securitySchemes:
    bearer_access_token:
      type: http
      scheme: bearer

```

## Examples



**Request**

```json
{}
```

**Response**

```json
{
  "errorText": "string",
  "code": "string",
  "expires_in": 1,
  "idToken": "string"
}
```

**SDK Code**

```python
import requests

url = "https://live.tradovateapi.com/v1/auth/shortgrantcode"

payload = {}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://live.tradovateapi.com/v1/auth/shortgrantcode';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://live.tradovateapi.com/v1/auth/shortgrantcode"

	payload := strings.NewReader("{}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://live.tradovateapi.com/v1/auth/shortgrantcode")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://live.tradovateapi.com/v1/auth/shortgrantcode")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://live.tradovateapi.com/v1/auth/shortgrantcode', [
  'body' => '{}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://live.tradovateapi.com/v1/auth/shortgrantcode");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://live.tradovateapi.com/v1/auth/shortgrantcode")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```