> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://partner.ninjatrader.com/eval/overview/conformance-testing/stage-1-authentication/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://partner.ninjatrader.com/_mcp/server. # Stage 1: Authentication Authentication is the foundation of all API interactions with the NinjaTrader Partner API. This stage ensures your integration can properly authenticate and maintain secure access to the platform. ## Overview The authentication stage validates that your application can: * Request access tokens using valid credentials * Renew access tokens to maintain active sessions * Handle authentication errors gracefully Before you begin, review the [Rate Limits](/partner-api/core-concepts/rate-limits). ## Required Tests ### 1. Access Token Request **Endpoint:** `POST /auth/accesstokenrequest` ### Response (200) ```json { "errorText": "string", "hibpHint": "EmailAndPasswordCompromised", "accessToken": "string", "expirationTime": "2024-01-15T09:30:00Z", "passwordExpirationTime": "2024-01-15T09:30:00Z", "userStatus": "Active", "userId": 1, "name": "string", "hasLive": true, "hasSimPlus": true, "showKIDs": true } ``` **Purpose:** Obtain an access token for API authentication. **Test Steps:** 1. Send a POST request to `/auth/accesstokenrequest` with valid credentials 2. Verify the response contains a valid access token 3. Verify the response includes an expiration time 4. Test with invalid credentials to ensure proper error handling **Example Request:** ```javascript const response = await fetch('https://demo.tradovateapi.com/v1/auth/accesstokenrequest', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ name: 'your_username', password: 'your_password', appId: 'your_app_id', appVersion: '1.0.0', cid: 'your_cid', sec: 'your_sec' }) }); const data = await response.json(); ``` **Expected Response:** ```json { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expirationTime": "2024-01-01T12:00:00.000Z" } ``` **Validation Criteria:** * ✅ Access token is returned and non-empty * ✅ Expiration time is included and valid * ✅ Response time is under 2 seconds * ✅ Invalid credentials return appropriate error (401 Unauthorized) ### 2. Access Token Renewal **Endpoint:** `GET /auth/renewaccesstoken` ### Response (200) ```json { "errorText": "string", "hibpHint": "EmailAndPasswordCompromised", "accessToken": "string", "expirationTime": "2024-01-15T09:30:00Z", "passwordExpirationTime": "2024-01-15T09:30:00Z", "userStatus": "Active", "userId": 1, "name": "string", "hasLive": true, "hasSimPlus": true, "showKIDs": true } ``` **Purpose:** Extend an existing access token without creating a new session. **Test Steps:** 1. Obtain a valid access token using `/auth/accesstokenrequest` 2. Use the access token to call `/auth/renewaccesstoken` before expiration 3. Verify the response contains a new access token 4. Verify the new token has an extended expiration time 5. Test with an expired token to ensure proper error handling **Example Request:** ```javascript const response = await fetch('https://demo.tradovateapi.com/v1/auth/renewaccesstoken', { method: 'GET', headers: { 'Authorization': `Bearer ${accessToken}`, 'Accept': 'application/json' } }); const data = await response.json(); ``` **Expected Response:** ```json { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expirationTime": "2024-01-01T18:00:00.000Z" } ``` **Validation Criteria:** * ✅ New access token is returned and different from original * ✅ New expiration time is extended beyond original * ✅ Response time is under 1 second * ✅ Expired tokens return appropriate error (401 Unauthorized) ## Error Handling Your implementation must handle the following error scenarios: ### Common Error Responses **Invalid Credentials (401):** ```json { "error": "Invalid credentials", "message": "The provided username or password is incorrect" } ``` **Token Expired (401):** ```json { "error": "Token expired", "message": "The access token has expired and cannot be renewed" } ``` **Rate Limiting (429):** ```json { "error": "Rate limit exceeded", "message": "Too many requests. Please try again later." } ``` A `429` is the user-level rate limit, distinct from a penalty ticket, which arrives as an `HTTP 200`. See [Penalty Tickets](/overview/core-concepts/penalty-tickets). ## Best Practices 1. **Token Storage**: Store access tokens securely and never log them 2. **Token Refresh**: Implement automatic token renewal before expiration 3. **Error Handling**: Implement retry logic with exponential backoff 4. **Monitoring**: Log authentication failures for debugging 5. **Session Management** * Should use a single access token across all API powered services. * Should prefer using the `/auth/renewaccesstoken` endpoint to renew the token instead of requesting a new one. * Should request a new token only if the previous one has expired and renewal fails. ## Testing Checklist * [ ] Access token request succeeds with valid Credentials * [ ] If implementing retry logic, don't retry on the case `401: Invalid Credentials` * [ ] Access token renewal succeeds with valid token * [ ] Access token renewal fails with expired token * [ ] Response times meet performance requirements * [ ] Error responses are properly handled * [ ] Session management is properly implemented ## Next Steps After completing Stage 1 authentication tests, proceed to [Stage 2: Websocket Management](/overview/conformance-testing/stage-2-websocket-management).