> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hyperprop.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a partner session

> Exchanges partner app credentials + a trader API key for a short-lived
bearer token. The key identifies the trader; an optional `email` must match
its owner. With `firm`, the token only acknowledges the trader's accounts at
that firm: accounts at other firms are invisible to the session, for reads,
writes and WebSocket events alike. Without `firm`, the session covers the
trader's accounts at every firm.



## OpenAPI

````yaml /api-reference/trade-openapi.json post /trade/partner/session
openapi: 3.0.3
info:
  contact:
    name: Hyperprop
    url: https://hyperprop.com
  description: >
    # Hyperprop Trade API


    Authenticated trading API for prop firm evaluations. Execute trades, manage
    positions, and monitor account performance in real-time.


    ---


    ## Introduction


    The Hyperprop Trade API is the trading engine itself: it executes orders,
    tracks positions, and enforces trading rules in real time.


    ### What You Can Do


    - **Execute trades** with market, limit, and stop orders

    - **Monitor positions** with real-time P&L updates

    - **Stream events** via WebSocket for instant order fills, position changes,
    and rule violations

    - **Track multiple accounts** across different prop firms simultaneously


    ---


    ## Quick Start


    ```javascript

    // 1. Get a partner session token from POST /trade/partner/session

    const token = 'your-session-token';


    // 2. Fetch your accounts

    const res = await fetch('/trade/accounts', {
      headers: { 'Authorization': `Bearer ${token}` }
    });

    const { data: accounts } = await res.json();


    // 3. Place a trade

    const order = await fetch('/trade/order', {
      method: 'POST',
      headers: { 
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        account_id: accounts[0].id,
        contract: 'ESH6',
        side: 'buy',
        quantity: 1
      })
    });

    ```


    ---


    ## Authentication


    Every endpoint except `POST /trade/partner/session` requires a bearer token.
    Third-party apps use the partner session token returned by that endpoint.


    **Header (recommended):**

    ```http

    Authorization: Bearer <your-session-token>

    ```


    **Query parameter (for WebSocket):**

    ```text

    /trade/ws?token=<your-session-token>

    ```


    ---


    ## Response Format


    All responses follow this structure:


    ### Success Response

    ```json

    {
      "success": true,
      "data": { ... },
      "error": null,
      "code": null
    }

    ```


    ### Error Response

    ```json

    {
      "success": false,
      "data": null,
      "error": "Human readable error message",
      "code": "ERROR_CODE"
    }

    ```


    ---


    ## Error Codes


    ### Authentication & Access


    | Code | HTTP Status | Description |

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

    | `UNAUTHORIZED` | 401 | Invalid or missing JWT token |

    | `FORBIDDEN` | 403 | Account does not belong to you |

    | `NO_ACCOUNT` | 404 | No trading account found for user |

    | `NOT_FOUND` | 404 | Resource not found (order, position, command, etc.) |

    | `ENGINE_AT_CAPACITY` | 400 | Too many commands are already in flight; the
    order was NOT accepted. Retry after a short backoff — the same
    `x-idempotency-key` is safe to reuse because nothing was queued. |


    ### Trading


    | Code | HTTP Status | Description |

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

    | `TRADING_DISABLED` | 403 | Account status prevents trading (passed or
    failed) |

    | `MARKET_NOT_ENABLED` | 403 | Account does not have access to this
    contract's exchange (e.g., trying to trade COMEX contract on a CME-only
    account) |

    | `INVALID_SIDE` | 400 | Side must be `buy` or `sell` |

    | `INVALID_CONTRACT` | 400 | Contract symbol is unknown or not active |

    | `INVALID_QUANTITY` | 400 | Quantity must be greater than 0 |

    | `MISSING_PRICE` | 400 | Limit/stop orders require a `price` field |

    | `INVALID_TAKE_PROFIT` | 400 | Take-profit price is invalid (<=0, bad tick,
    or wrong side relative to entry) |

    | `INVALID_STOP_LOSS` | 400 | Stop-loss price is invalid (<=0, bad tick, or
    wrong side relative to entry) |

    | `BRACKET_ATTACH_FAILED` | 400 | Bracket order validation failed (invalid
    TP/SL combination) |

    | `INVALID_REQUEST` | 400 | Missing or invalid request fields |


    ### Lockout


    | Code | HTTP Status | Description |

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

    | `LOCKOUT_ACTIVE` | 403 | Account is currently locked — trading actions are
    blocked |

    | `INVALID_LOCKOUT_MODE` | 400 | Mode must be one of: `hours`,
    `trading_day`, `timed`, `session`, `trade_clock` |

    | `INVALID_LOCKOUT_DURATION` | 400 | Duration is invalid (e.g. hours <= 0,
    or unknown timed_duration value) |

    | `LOCKOUT_NOT_REMOVABLE` | 403 | Lockout cannot be removed by user — either
    it is timed (wait for expiry), trade_clock with open exposure, or
    admin-locked |


    ### Idempotency & Infrastructure


    | Code | HTTP Status | Description |

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

    | `DUPLICATE_REQUEST` | 409 | Same `x-idempotency-key` was already used for
    this operation |

    | `RATE_LIMITED` | 429 | Too many requests — account exceeded the allowed
    rate for this action |

    | `REDIS_UNAVAILABLE` | 503 | Service temporarily unavailable — the command
    was not accepted; retry with backoff |

    | `INTERNAL_ERROR` | 500 | Unexpected server error |


    ---


    ## Order Types


    | Type | Description | Required Fields |

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

    | `market` | Execute immediately at best price | account_id, contract, side,
    quantity |

    | `limit` | Execute when price reaches level | + price |

    | `stop` | Trigger market order at stop price | + price |


    ### Order Request Examples


    **Market Order:**

    ```json

    {
      "account_id": "550e8400-e29b-41d4-a716-446655440000",
      "contract": "ESH6",
      "side": "buy",
      "quantity": 2
    }

    ```


    **Limit Order:**

    ```json

    {
      "account_id": "550e8400-e29b-41d4-a716-446655440000",
      "contract": "ESH6",
      "side": "sell",
      "quantity": 1,
      "order_type": "limit",
      "price": 6050.25
    }

    ```


    **Stop Order:**

    ```json

    {
      "account_id": "550e8400-e29b-41d4-a716-446655440000",
      "contract": "ESH6",
      "side": "sell",
      "quantity": 1,
      "order_type": "stop",
      "price": 6000.00
    }

    ```


    ### Order Response

    ```json

    {
      "success": true,
      "data": {
        "id": "order-uuid",
        "user_id": "user-uuid",
        "account_id": "account-uuid",
        "contract": "ESH6",
        "side": "buy",
        "order_type": "market",
        "quantity": 2,
        "filled_quantity": 2,
        "limit_price": null,
        "stop_price": null,
        "status": "filled",
        "filled_price": 6045.50,
        "filled_at": "2024-01-15T10:30:00Z",
        "processed_at": "2024-01-15T10:30:00Z",
        "reject_reason": null
      }
    }

    ```


    ---


    ## Account Status


    The Trade API returns these status values via REST and WebSocket. The
    Platform API and webhooks use the equivalent values in the second column.


    | Trade API status | Platform API value | Can Trade | Description |

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

    | `Active` | `not_started` | ✅ | Account created, not yet traded |

    | `InProgress` | `in_progress` | ✅ | Actively trading |

    | `Completed` | `passed` | ❌ | Profit target reached — account passed
    (flat-confirmed) |

    | `Violated` | `failed` | ❌ | Rule violation — auto-liquidated, trading
    disabled |

    | `Violated` | `expired` | ❌ | Account expired — treated same as failed |


    ---


    ## Position Schema


    ```json

    {
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "contract": "ESH6",
      "quantity": 2,
      "avg_entry_price": 6045.50,
      "unrealized_pnl": 125.00,
      "realized_pnl": 0.00,
      "opened_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:35:00Z"
    }

    ```


    - `quantity`: Positive = long, negative = short

    - `unrealized_pnl`: Current P&L if closed now

    - `realized_pnl`: Gross P&L from partial closes (does NOT include fees)


    ---


    ## Trade Schema


    ```json

    {
      "id": "trade-uuid",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "order_id": "order-uuid",
      "contract": "ESH6",
      "side": "buy",
      "price": 6045.50,
      "quantity": 2,
      "realized_pnl": 0.00,
      "timestamp": "2024-01-15T10:30:00Z"
    }

    ```


    ---


    ## WebSocket


    Real-time streaming of trading events.


    ### Connection


    ```text

    wss://{host}/trade/ws?token=JWT_TOKEN

    wss://{host}/trade/ws?token=JWT_TOKEN&account_id=ACCOUNT_UUID

    ```


    Replace `{host}` with `api.hyperprop.com`.


    ### Commands


    | Command | Description |

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

    | `{"action": "subscribe", "account_id": "uuid"}` | Filter to specific
    account |

    | `{"action": "subscribe", "account_id": "*"}` | Receive all accounts
    (default) |

    | `{"action": "unsubscribe", "account_id": "uuid"}` | Stop receiving account
    events |

    | `{"action": "ping"}` | Keep-alive |


    ### Events


    **Welcome (on connect):**

    ```json

    {
      "type": "Welcome",
      "user_id": "user-uuid",
      "accounts": [
        {"id": "acc-1", "account_number": "HP-12345", "balance": 50000, "status": "Active"}
      ],
      "subscribed_to": "*",
      "open_positions": 1,
      "pending_orders": 0
    }

    ```


    **OrderSubmitted:**

    ```json

    {
      "type": "OrderSubmitted",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "order": {
        "id": "order-uuid",
        "contract": "ESH6",
        "product": "CME",
        "symbol": "ES",
        "side": "buy",
        "order_type": "market",
        "quantity": 1,
        "status": "filled",
        "filled_price": 6045.50,
        "reason": "User submitted market order"
      }
    }

    ```


    **OrderFilled:**

    ```json

    {
      "type": "OrderFilled",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "order": {
        "id": "order-uuid",
        "contract": "ESH6",
        "product": "CME",
        "symbol": "ES",
        "side": "buy",
        "order_type": "limit",
        "quantity": 1,
        "status": "filled",
        "filled_price": 6045.50,
        "reason": "Limit price reached"
      },
      "trade": { "id": "trade-uuid", "price": 6045.50, "quantity": 1, "realized_pnl": 0 }
    }

    ```


    **OrderCancelled:**

    ```json

    {
      "type": "OrderCancelled",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "order": {
        "id": "order-uuid",
        "contract": "ESH6",
        "product": "CME",
        "symbol": "ES",
        "side": "buy",
        "order_type": "limit",
        "quantity": 1,
        "status": "cancelled",
        "reason": "User requested cancellation"
      }
    }

    ```


    **PositionUpdate:**

    ```json

    {
      "type": "PositionUpdate",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "position": { "contract": "ESH6", "quantity": 1, "unrealized_pnl": 125.00, ... }
    }

    ```


    **AccountUpdate:**

    ```json

    {
      "type": "AccountUpdate",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "current_balance": 50000.00,
      "equity": 50125.00,
      "high_water_mark": 50125.00,
      "unrealized_pnl": 125.00,
      "total_pnl": 125.00,
      "daily_pnl": 125.00,
      "current_drawdown": 0.00,
      "status": "InProgress",
      "violation_reason": null
    }

    ```


    **RuleViolation:**

    ```json

    {
      "type": "RuleViolation",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "rule": "max_loss",
      "reason": "Max Loss exceeded: $2500 loss >= $2500 limit"
    }

    ```


    **Notification:**

    ```json

    {
      "type": "Notification",
      "user_id": "user-uuid",
      "account_id": "account-uuid",
      "level": "warning",
      "title": "Copy Trade Failed",
      "message": "Failed to copy BUY 1 ESH6 to follower account: Order would exceed max contracts limit (15 > 10)",
      "category": "copy_trade"
    }

    ```


    Notification levels: `info`, `warning`, `error`


    Notification categories:

    - `copy_trade` - Copy trading events (failed copies, sync issues)

    - `order` - Order-related notifications (rejections, modifications)

    - `risk` - Risk management alerts (approaching limits)

    - `system` - System notifications (maintenance, updates)


    ### Example Client


    ```javascript

    const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';

    const ws = new
    WebSocket(`${protocol}//${location.host}/trade/ws?token=${jwt}&account_id=${accountId}`);


    ws.onopen = () => console.log('Connected');


    ws.onmessage = (e) => {
      const msg = JSON.parse(e.data);
      
      switch (msg.type) {
        case 'Welcome':
          console.log('Accounts:', msg.accounts);
          break;
        case 'OrderFilled':
          console.log(`Filled: ${msg.order.side} ${msg.order.quantity} ${msg.order.contract} @ ${msg.trade.price}`);
          break;
        case 'PositionUpdate':
          console.log(`P&L: $${msg.position.unrealized_pnl.toFixed(2)}`);
          break;
        case 'AccountUpdate':
          console.log(`Equity: $${msg.equity.toFixed(2)}, Drawdown: $${msg.drawdown.toFixed(2)}`);
          break;
        case 'RuleViolation':
          alert(`⚠️ ${msg.reason}`);
          break;
        case 'Notification':
          // Display notification to user based on level
          const icon = msg.level === 'error' ? '❌' : msg.level === 'warning' ? '⚠️' : 'ℹ️';
          console.log(`${icon} ${msg.title}: ${msg.message}`);
          // Show toast/notification in UI
          break;
      }
    };


    // Switch accounts dynamically

    ws.send(JSON.stringify({ action: 'subscribe', account_id:
    'other-account-uuid' }));

    ```


    ---


    ## Trading Rules


    Each account has rules that are checked in real-time:


    | Rule | Type | Description |

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

    | `profit_target` | $ | Target profit to pass evaluation |

    | `max_loss` | $ | Maximum total loss allowed |

    | `daily_loss` | $ | Maximum loss per day |

    | `max_drawdown` | % | Max drawdown from high water mark |

    | `daily_drawdown` | % | Max daily drawdown |

    | `max_contracts` | # | Maximum position size in mini-equivalent units |

    | `micro_conversion_ratio` | # | How many micros count as 1 unit toward
    `max_contracts` (default 10; e.g. 5 or 1 per firm plan) |


    When a rule is violated, positions are auto-liquidated and the account is
    marked as `Violated` or `Completed`.


    ---


    ## Contracts


    Supported CME futures contracts used by the engine:


    | Symbol | Name | Tick Size | Point Value |

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

    | ESH6 | E-mini S&P 500 | 0.25 | $12.50 |

    | NQH6 | E-mini Nasdaq | 0.25 | $5.00 |

    | YMH6 | E-mini Dow | 1.00 | $5.00 |

    | RTH6 | E-mini Russell | 0.10 | $5.00 |


    ---


    ## Rate Limits


    Rate limits are enforced **per trading account** using token buckets. Each
    action category has its own independent bucket, so order submissions don't
    compete with cancellations.


    ### Limits by Action


    | Action | Burst Capacity | Refill Rate | Applies To |

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

    | Order Submit | 10 | 10/sec | `POST /trade/order` |

    | Order Modify | 10 | 10/sec | `POST /trade/order/modify`,
    `/order/attach-bracket`, `/order/detach-bracket` |

    | Order Cancel | 20 | 20/sec | `POST /trade/order/cancel`,
    `/orders/cancel-all` |

    | Position Close | 5 | 2/sec | `POST /trade/position/close`,
    `/position/reverse`, `/close-all`, `/flatten` |

    | Copy Config | 10 | 2/sec | `POST /trade/copy/*` (create, update, toggle,
    delete) |

    | Lockout | 5 | 1/sec | `POST /trade/account/lockout`, `DELETE
    /trade/account/lockout` |


    Read endpoints (GET) are **not** rate limited.


    ### How It Works


    Each account starts with a full bucket (e.g., 10 tokens for order submit).
    Every request consumes 1 token. Tokens refill continuously at the specified
    rate. If the bucket is empty, the request is rejected with HTTP 429.


    ### Rate Limit Response (HTTP 429)


    ```json

    {
      \"success\": false,
      \"data\": null,
      \"error\": \"Rate limit exceeded for order submissions. Max 10 requests per second per account. Retry after 0.1 seconds.\",
      \"code\": \"RATE_LIMITED\"
    }

    ```


    ### Headers


    Successful requests do not include rate limit headers to minimize overhead.
    On 429 responses, inspect the error message for retry timing.


    ### Organization Overrides


    Organizations can have custom rate limits (e.g., higher limits for copy
    trading leaders). Contact the Hyperprop team for custom configurations.


    ### Important Notes


    - Limits are per **account**, not per user. A user with 5 accounts has 5
    independent buckets per action.

    - Different action categories have independent buckets. Exhausting order
    submit tokens does not affect cancellation tokens.

    - WebSocket events are **not** rate limited (server → client push).
  license:
    name: Proprietary
  title: Hyperprop Trade API
  version: 1.0.0
servers:
  - url: https://api.hyperprop.com
    description: Production
security: []
tags:
  - description: Trading account management - balances, equity, P&L, rules
    name: Account
  - description: Order submission, cancellation, and history
    name: Orders
  - description: Open positions and P&L tracking
    name: Positions
  - description: Real-time streaming of trading events
    name: WebSocket
paths:
  /trade/partner/session:
    post:
      tags:
        - Account
      summary: Create a partner session
      description: >-
        Exchanges partner app credentials + a trader API key for a short-lived

        bearer token. The key identifies the trader; an optional `email` must
        match

        its owner. With `firm`, the token only acknowledges the trader's
        accounts at

        that firm: accounts at other firms are invisible to the session, for
        reads,

        writes and WebSocket events alike. Without `firm`, the session covers
        the

        trader's accounts at every firm.
      operationId: partner_session
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PartnerSessionRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              example:
                code: null
                data:
                  access_token: eyJhbGciOiJIUzI1NiIs...
                  account_ids:
                    - b2e571d1-54b6-42a5-a09a-ccd543594567
                    - 9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a
                  expires_in: 900
                  organization_id: null
                  organization_name: null
                  organization_slug: null
                  organizations:
                    - account_ids:
                        - b2e571d1-54b6-42a5-a09a-ccd543594567
                      id: 7e8f9a0b-4c5d-4e6f-8a9b-0c1d2e3f4a51
                      name: FakePropFirm
                      slug: fakepropfirm
                    - account_ids:
                        - 9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a
                      id: 0c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f
                      name: OtherPropFirm
                      slug: otherpropfirm
                  token_type: Bearer
                error: null
                success: true
              schema:
                $ref: '#/components/schemas/PartnerSessionResponse'
          description: Session created
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
          description: Invalid app credentials, API key or email
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
          description: Trader has no accounts (at this firm, when one was given)
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
          description: Too many failed attempts
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
          description: Email or firm details could not be checked; retry
components:
  schemas:
    PartnerSessionRequest:
      description: Request body for the partner session exchange.
      properties:
        api_key:
          description: Raw trader API key ("hpk_..."). Identifies the trader.
          type: string
        app_id:
          description: Partner application id issued by Hyperprop.
          format: uuid
          type: string
        app_secret:
          description: Raw partner application secret.
          type: string
        email:
          description: >-
            Optional. The trader's Hyperprop login email; when sent it must
            match

            the key's owner (case-insensitive).
          nullable: true
          type: string
        firm:
          description: |-
            Optional. Limits the session to one firm: organization id, slug or
            name. When omitted, the session covers all of the trader's firms.
          nullable: true
          type: string
      required:
        - app_id
        - app_secret
        - api_key
      type: object
    PartnerSessionResponse:
      properties:
        access_token:
          description: Bearer token for subsequent requests, limited to `account_ids`.
          type: string
        account_ids:
          description: Trader accounts this session can see and trade.
          items:
            format: uuid
            type: string
          type: array
        expires_in:
          description: Seconds until the token expires; re-login to refresh.
          format: int64
          minimum: 0
          type: integer
        organization_id:
          description: |-
            Set when every account in the session belongs to one firm; null when
            the session spans several firms.
          format: uuid
          nullable: true
          type: string
        organization_name:
          nullable: true
          type: string
        organization_slug:
          nullable: true
          type: string
        organizations:
          description: Every firm the session reaches, with the trader's accounts at each.
          items:
            $ref: '#/components/schemas/PartnerSessionOrganization'
          type: array
        token_type:
          type: string
      required:
        - access_token
        - token_type
        - expires_in
        - organizations
        - account_ids
      type: object
    ApiErrorResponse:
      description: Standard API error response
      example:
        code: ERROR_CODE
        data: null
        error: Error message describing what went wrong
        success: false
      properties:
        code:
          description: Machine-readable error code
          example: NO_ACCOUNT
          nullable: true
          type: string
        data:
          description: Always null for errors
          nullable: true
        error:
          description: Human-readable error message
          example: No trading account found for this user
          nullable: true
          type: string
        success:
          description: Always false for errors
          example: false
          type: boolean
      required:
        - success
      type: object
    PartnerSessionOrganization:
      properties:
        account_ids:
          description: The trader's accounts at this firm that the session can reach.
          items:
            format: uuid
            type: string
          type: array
        id:
          format: uuid
          type: string
        name:
          type: string
        slug:
          type: string
      required:
        - id
        - name
        - slug
        - account_ids
      type: object

````

## Related topics

- [Authentication](/authentication.md)
- [Partner integrations](/guides/partner-integrations.md)
- [Generate a trader API key for third-party access](/trade-api/account/generate-a-trader-api-key-for-third-party-access.md)
- [Introduction](/introduction.md)
- [Revoke one of the trader's API keys](/trade-api/account/revoke-one-of-the-traders-api-keys.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.