> ## 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 trading account

> Create a new trading account for a trader. This is the main endpoint for provisioning accounts after a purchase.

**Identify the trader by `traderId` OR `email` (exactly one).** If you have the Hyperprop user ID, pass `traderId`. If you only have the trader's email, pass `email` and it's resolved to their Hyperprop user ID server-side. Either way, the trader's email and name are auto-resolved — you don't need to pass name.

**What happens:**
1. The trader is looked up by their Hyperprop user ID (or by email, which is resolved to a user ID)
2. A purchase record is created to track the transaction
3. A trading account is created with `not_started` status
4. The account is immediately visible in the trader's Hyperprop dashboard

**Account types:**

| Type | Description |
|------|-------------|
| `evaluation` | Challenge account — trader must hit profit target to pass |
| `sim_funded` | Simulated funded account (post-evaluation) |
| `competition` | Competition account — traders compete against each other |

**Required fields:**
| Field | Description |
|-------|-------------|
| `type` | Account type: `evaluation`, `sim_funded`, or `competition` |
| `tradingPlanId` | Which plan this account is for (get IDs from GET /trading-plans) |
| `customerId` | Your customer's ID in YOUR system — stable per customer, not per order (see soulbound identities below) |
| `traderId` **or** `email` **or** `unassigned` | How to identify the trader — provide **exactly one** (see below) |

**Identifying the trader — three modes (provide exactly one):**
- `traderId` — the Hyperprop platform user ID (UUID). Use this when you already have it.
- `email` — the email of an **existing** Hyperprop user. It's resolved to the user ID server-side. If the email has no Hyperprop account you get `404 TRADER_NOT_FOUND`.
- `unassigned: true` — **import-key mode.** The account is created with no trader and the response contains a one-time `importKey` (serial-key style, e.g. `HP-7K3QF-9XT2M-4WHRD`). The same key is in the `account.created` webhook (`data.importKey`) so you can recover it if you miss the response. Hand the key to your customer — order-confirmation email, member area, wherever — and they redeem it inside the Hyperprop app under **Trading Accounts → Import**. When they do, the account links to their user, an `account.imported` webhook fires with their email + the key + a timestamp for your verification, and billing + CME entitlement start at that moment (not at creation). For many accounts at once use `POST /trading-accounts/bulk`.

**Soulbound identities (keyed on `customerId` — always active):**
Every account carries your `customerId`, and each customer is pinned to exactly ONE Hyperprop login (a *binding*):
- **Unassigned accounts:** the FIRST import-key redemption for a `customerId` creates the binding. Every later import for the same customer must be redeemed by that same login — a different login is refused with `409 TRADER_ALREADY_BOUND` and shown a masked hint of the correct email.
- **Directly-linked accounts (`traderId`/`email`):** creating the account creates the binding. If the `customerId` is ALREADY bound to a different Hyperprop login, the create is refused with `409 CUSTOMER_ALREADY_BOUND` — link the account to the bound login instead, or rebind the customer first.

Result: one customer, one Hyperprop identity, all their accounts in one place — no clashes. You learn the bound login from the `account.imported` webhook (`import.importedEmail`). Inspect bindings via `GET /trader-bindings`; a customer who lost their Hyperprop login is moved to a new one via `POST /trader-bindings/{bindingId}/rebind` (which also fails all still-active accounts on the old login — a binding is never left dangling).

For `traderId`/`email`, display name and avatar are resolved automatically from the trader's Hyperprop profile.

The response's `importKey` field is `null` for directly-linked accounts and holds the redemption key for unlinked ones. An unlinked account can be reissued a fresh key with `POST /trading-accounts/{accountId}/unlink` — but only before trading starts.

**Where to get the `traderId`:**
- From GET /organization/traders → the `userId` field (for traders who already have accounts with you)
- From the Hyperprop onboarding/purchase flow (for new traders)
- Or skip it entirely and just pass the trader's `email`.

**Optional fields:**
| Field | Description |
|-------|-------------|
| `markets` | Deprecated compatibility field. Omit it. Every account receives the complete CME Group bundle (`CME`, `CBOT`, `NYMEX`, `COMEX`) regardless of a legacy partial value. |
| `tradingRuleId` | Override the plan's default rules |
| `accountNumber` | Your own account number (e.g., `ACME-100K-7845`). If omitted, auto-generated as `ACC-XXXXXXXX` |
| `initialBalance` | Override the plan's default balance |
| `traderExternalId` | **Queryable external reference for the trader.** Filter later with `GET /traders?externalUserId=...`; returned as `trader.externalUserId` |
| `metadata` | Add notes, tags, or any custom data |
| `purchase` | Payment details (price paid, currency, payment IDs) |

**customerId vs metadata:**
`metadata` is stored verbatim but only filterable by exact key:value. `customerId` (account) and `traderExternalId` (trader) are first-class fields you can filter by directly — use them to reconcile webhooks and imports against your own IDs without email round-trips or metadata scanning. Both also appear in every webhook payload (`data.customerId`, `data.trader.externalUserId`). Webhook payloads also carry `data.externalRef` (same value) for integrations built before the rename — treat it as deprecated.

**Markets & billing:**
CME Group depth-of-book market data is one inseparable bundle. Every account
receives access to CME, CBOT, NYMEX, and COMEX. Partners cannot select or pay
for individual exchanges.

Billing is:

`platform_price_per_user + market_data_price_cme`

The `market_data_price_cme` organization setting is the price of the **full
CME Group bundle**, despite the legacy column name. CBOT, NYMEX, and COMEX are
not added as separate line items. The cycle price snapshot uses:

```json
{ "platform": 0.25, "CME_GROUP": 6.75 }
```

The legacy `markets` request field remains accepted to avoid breaking older
integrations, but any partial input such as `["CME"]` is normalized to
`["CME", "CBOT", "NYMEX", "COMEX"]` in the stored account and response.

**Minimal example (4 fields, all markets):**
```json
{
  "type": "evaluation",
  "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
  "traderId": "f994bc02-343c-4026-8a57-afc085eca8d5",
  "customerId": "your-customer-4471"
}
```

**Legacy input normalization example:**
```json
{
  "type": "evaluation",
  "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
  "traderId": "f994bc02-343c-4026-8a57-afc085eca8d5",
  "markets": ["CME"]
}
```

The account still receives the full four-exchange bundle. New integrations
should omit `markets`.

**Example identifying the trader by email (instead of traderId):**
```json
{
  "type": "evaluation",
  "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
  "email": "john@example.com",
  "customerId": "cust_abc123"
}
```

**Full example with purchase:**
```json
{
  "type": "evaluation",
  "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
  "email": "john@example.com",
  "customerId": "cust_abc123",
  "accountNumber": "ACME-100K-7845",
  "metadata": {
    "notes": "Referred by Mike - 10% discount",
    "tags": ["referral", "discount"]
  },
  "purchase": {
    "pricePaid": 449,
    "currency": "USD",
    "metadata": {
      "stripePaymentId": "pi_3ABC123",
      "discountCode": "MIKE10"
    }
  }
}
```

**Permissions:**
Only organization **admins** can create accounts.

**Error responses:**
Every error returns the same shape — `{ statusCode, error, message, code }` — so you can switch on `code`:

| Status | `code` | When |
|:---:|------|------|
| 400 | `VALIDATION_ERROR` | Payload failed validation — including a missing `customerId`, sending more than one (or none) of `traderId` / `email` / `unassigned`, or a malformed email |
| 400 | `PLAN_NOT_ACTIVE` | The trading plan exists but is not active |
| 400 | `NO_TRADING_RULE` | No `tradingRuleId` was given and the plan has no default rule |
| 401 | `UNAUTHORIZED` | Missing/invalid API key or session |
| 403 | `INSUFFICIENT_PERMISSIONS` / `NOT_ADMIN` | The API key (or user) lacks admin/write permission |
| 404 | `TRADER_NOT_FOUND` | The `traderId` or `email` doesn't match an existing Hyperprop user |
| 404 | `PLAN_NOT_FOUND` | `tradingPlanId` not found for your organization |
| 404 | `RULE_NOT_FOUND` | `tradingRuleId` not found for your organization |
| 409 | `CUSTOMER_ALREADY_BOUND` | The `customerId` is already soulbound to a DIFFERENT Hyperprop login than the `traderId`/`email` you sent. Link the account to the bound login, or rebind the customer first (`POST /trader-bindings/{bindingId}/rebind`) |
| 500 | `PURCHASE_CREATE_ERROR` / `ACCOUNT_CREATE_ERROR` | Unexpected server error while provisioning |

Note: `traderId`, `email`, and `unassigned` are mutually exclusive — send exactly one. Sending several, or none, is a `400 VALIDATION_ERROR`. `customerId` is always required (`externalRef` is accepted as a deprecated alias).



## OpenAPI

````yaml /api-reference/openapi.json post /v1/organization/trading-accounts
openapi: 3.0.0
info:
  title: Hyperprop Platform API
  version: 1.0.0
  description: >-
    REST API for the Hyperprop Trading Platform — provision evaluation and
    funded trading accounts, manage traders and plans, react to account
    lifecycle events via signed webhooks, and reconcile billing. Built for prop
    firms integrating from their own backend.


    Authentication, quick start, error handling, idempotency, pagination, custom
    metadata, webhooks, and the MCP connector are documented at
    https://docs.hyperprop.com.
  x-logo:
    url: https://app.hyperprop.com/logo-icon.svg
    altText: Hyperprop
    href: https://hyperprop.com
servers:
  - url: https://api.hyperprop.com/platform
    description: Production
security: []
tags:
  - name: Trading accounts
    description: Create, update, and inspect trading accounts.
  - name: Traders
    description: Look up and update the traders in your organization.
  - name: Trading plans
    description: Define the plans you sell.
  - name: Trading rules
    description: Define how accounts are evaluated.
  - name: Lockouts
    description: Pause and resume trading on an account.
  - name: Payouts
    description: Check payout eligibility and record payouts.
  - name: Purchases
    description: Purchases recorded for your organization.
  - name: Time Machine
    description: Restore accounts to an earlier trading day or instant.
  - name: Webhooks
    description: Register webhook endpoints and inspect deliveries.
  - name: Events
    description: Your organization's event history and real-time event stream.
  - name: Reconciliation
    description: Balances, end-of-day snapshots, and fills for reconciliation.
  - name: Analytics
    description: Organization performance and plan economics.
  - name: Billing
    description: 'Your Hyperprop bill: activity, billing cycles, and forecasts.'
  - name: Team and roles
    description: Manage dashboard access for your staff.
  - name: API keys
    description: Manage your organization's API key.
  - name: Logs and health
    description: API request logs, the audit log, and integration health.
  - name: Organization profile
    description: Your organization's profile and logo.
  - name: Support
    description: Open and follow up on support tickets.
  - name: Partner access
    description: >-
      Read a trader's journal as an approved partner app, with the trader's own
      key.
paths:
  /v1/organization/trading-accounts:
    post:
      tags:
        - Trading accounts
      summary: Create a trading account
      description: >-
        Create a new trading account for a trader. This is the main endpoint for
        provisioning accounts after a purchase.


        **Identify the trader by `traderId` OR `email` (exactly one).** If you
        have the Hyperprop user ID, pass `traderId`. If you only have the
        trader's email, pass `email` and it's resolved to their Hyperprop user
        ID server-side. Either way, the trader's email and name are
        auto-resolved — you don't need to pass name.


        **What happens:**

        1. The trader is looked up by their Hyperprop user ID (or by email,
        which is resolved to a user ID)

        2. A purchase record is created to track the transaction

        3. A trading account is created with `not_started` status

        4. The account is immediately visible in the trader's Hyperprop
        dashboard


        **Account types:**


        | Type | Description |

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

        | `evaluation` | Challenge account — trader must hit profit target to
        pass |

        | `sim_funded` | Simulated funded account (post-evaluation) |

        | `competition` | Competition account — traders compete against each
        other |


        **Required fields:**

        | Field | Description |

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

        | `type` | Account type: `evaluation`, `sim_funded`, or `competition` |

        | `tradingPlanId` | Which plan this account is for (get IDs from GET
        /trading-plans) |

        | `customerId` | Your customer's ID in YOUR system — stable per
        customer, not per order (see soulbound identities below) |

        | `traderId` **or** `email` **or** `unassigned` | How to identify the
        trader — provide **exactly one** (see below) |


        **Identifying the trader — three modes (provide exactly one):**

        - `traderId` — the Hyperprop platform user ID (UUID). Use this when you
        already have it.

        - `email` — the email of an **existing** Hyperprop user. It's resolved
        to the user ID server-side. If the email has no Hyperprop account you
        get `404 TRADER_NOT_FOUND`.

        - `unassigned: true` — **import-key mode.** The account is created with
        no trader and the response contains a one-time `importKey` (serial-key
        style, e.g. `HP-7K3QF-9XT2M-4WHRD`). The same key is in the
        `account.created` webhook (`data.importKey`) so you can recover it if
        you miss the response. Hand the key to your customer —
        order-confirmation email, member area, wherever — and they redeem it
        inside the Hyperprop app under **Trading Accounts → Import**. When they
        do, the account links to their user, an `account.imported` webhook fires
        with their email + the key + a timestamp for your verification, and
        billing + CME entitlement start at that moment (not at creation). For
        many accounts at once use `POST /trading-accounts/bulk`.


        **Soulbound identities (keyed on `customerId` — always active):**

        Every account carries your `customerId`, and each customer is pinned to
        exactly ONE Hyperprop login (a *binding*):

        - **Unassigned accounts:** the FIRST import-key redemption for a
        `customerId` creates the binding. Every later import for the same
        customer must be redeemed by that same login — a different login is
        refused with `409 TRADER_ALREADY_BOUND` and shown a masked hint of the
        correct email.

        - **Directly-linked accounts (`traderId`/`email`):** creating the
        account creates the binding. If the `customerId` is ALREADY bound to a
        different Hyperprop login, the create is refused with `409
        CUSTOMER_ALREADY_BOUND` — link the account to the bound login instead,
        or rebind the customer first.


        Result: one customer, one Hyperprop identity, all their accounts in one
        place — no clashes. You learn the bound login from the
        `account.imported` webhook (`import.importedEmail`). Inspect bindings
        via `GET /trader-bindings`; a customer who lost their Hyperprop login is
        moved to a new one via `POST /trader-bindings/{bindingId}/rebind` (which
        also fails all still-active accounts on the old login — a binding is
        never left dangling).


        For `traderId`/`email`, display name and avatar are resolved
        automatically from the trader's Hyperprop profile.


        The response's `importKey` field is `null` for directly-linked accounts
        and holds the redemption key for unlinked ones. An unlinked account can
        be reissued a fresh key with `POST /trading-accounts/{accountId}/unlink`
        — but only before trading starts.


        **Where to get the `traderId`:**

        - From GET /organization/traders → the `userId` field (for traders who
        already have accounts with you)

        - From the Hyperprop onboarding/purchase flow (for new traders)

        - Or skip it entirely and just pass the trader's `email`.


        **Optional fields:**

        | Field | Description |

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

        | `markets` | Deprecated compatibility field. Omit it. Every account
        receives the complete CME Group bundle (`CME`, `CBOT`, `NYMEX`, `COMEX`)
        regardless of a legacy partial value. |

        | `tradingRuleId` | Override the plan's default rules |

        | `accountNumber` | Your own account number (e.g., `ACME-100K-7845`). If
        omitted, auto-generated as `ACC-XXXXXXXX` |

        | `initialBalance` | Override the plan's default balance |

        | `traderExternalId` | **Queryable external reference for the trader.**
        Filter later with `GET /traders?externalUserId=...`; returned as
        `trader.externalUserId` |

        | `metadata` | Add notes, tags, or any custom data |

        | `purchase` | Payment details (price paid, currency, payment IDs) |


        **customerId vs metadata:**

        `metadata` is stored verbatim but only filterable by exact key:value.
        `customerId` (account) and `traderExternalId` (trader) are first-class
        fields you can filter by directly — use them to reconcile webhooks and
        imports against your own IDs without email round-trips or metadata
        scanning. Both also appear in every webhook payload (`data.customerId`,
        `data.trader.externalUserId`). Webhook payloads also carry
        `data.externalRef` (same value) for integrations built before the rename
        — treat it as deprecated.


        **Markets & billing:**

        CME Group depth-of-book market data is one inseparable bundle. Every
        account

        receives access to CME, CBOT, NYMEX, and COMEX. Partners cannot select
        or pay

        for individual exchanges.


        Billing is:


        `platform_price_per_user + market_data_price_cme`


        The `market_data_price_cme` organization setting is the price of the
        **full

        CME Group bundle**, despite the legacy column name. CBOT, NYMEX, and
        COMEX are

        not added as separate line items. The cycle price snapshot uses:


        ```json

        { "platform": 0.25, "CME_GROUP": 6.75 }

        ```


        The legacy `markets` request field remains accepted to avoid breaking
        older

        integrations, but any partial input such as `["CME"]` is normalized to

        `["CME", "CBOT", "NYMEX", "COMEX"]` in the stored account and response.


        **Minimal example (4 fields, all markets):**

        ```json

        {
          "type": "evaluation",
          "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
          "traderId": "f994bc02-343c-4026-8a57-afc085eca8d5",
          "customerId": "your-customer-4471"
        }

        ```


        **Legacy input normalization example:**

        ```json

        {
          "type": "evaluation",
          "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
          "traderId": "f994bc02-343c-4026-8a57-afc085eca8d5",
          "markets": ["CME"]
        }

        ```


        The account still receives the full four-exchange bundle. New
        integrations

        should omit `markets`.


        **Example identifying the trader by email (instead of traderId):**

        ```json

        {
          "type": "evaluation",
          "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
          "email": "john@example.com",
          "customerId": "cust_abc123"
        }

        ```


        **Full example with purchase:**

        ```json

        {
          "type": "evaluation",
          "tradingPlanId": "550e8400-e29b-41d4-a716-446655440000",
          "email": "john@example.com",
          "customerId": "cust_abc123",
          "accountNumber": "ACME-100K-7845",
          "metadata": {
            "notes": "Referred by Mike - 10% discount",
            "tags": ["referral", "discount"]
          },
          "purchase": {
            "pricePaid": 449,
            "currency": "USD",
            "metadata": {
              "stripePaymentId": "pi_3ABC123",
              "discountCode": "MIKE10"
            }
          }
        }

        ```


        **Permissions:**

        Only organization **admins** can create accounts.


        **Error responses:**

        Every error returns the same shape — `{ statusCode, error, message, code
        }` — so you can switch on `code`:


        | Status | `code` | When |

        |:---:|------|------|

        | 400 | `VALIDATION_ERROR` | Payload failed validation — including a
        missing `customerId`, sending more than one (or none) of `traderId` /
        `email` / `unassigned`, or a malformed email |

        | 400 | `PLAN_NOT_ACTIVE` | The trading plan exists but is not active |

        | 400 | `NO_TRADING_RULE` | No `tradingRuleId` was given and the plan
        has no default rule |

        | 401 | `UNAUTHORIZED` | Missing/invalid API key or session |

        | 403 | `INSUFFICIENT_PERMISSIONS` / `NOT_ADMIN` | The API key (or user)
        lacks admin/write permission |

        | 404 | `TRADER_NOT_FOUND` | The `traderId` or `email` doesn't match an
        existing Hyperprop user |

        | 404 | `PLAN_NOT_FOUND` | `tradingPlanId` not found for your
        organization |

        | 404 | `RULE_NOT_FOUND` | `tradingRuleId` not found for your
        organization |

        | 409 | `CUSTOMER_ALREADY_BOUND` | The `customerId` is already soulbound
        to a DIFFERENT Hyperprop login than the `traderId`/`email` you sent.
        Link the account to the bound login, or rebind the customer first (`POST
        /trader-bindings/{bindingId}/rebind`) |

        | 500 | `PURCHASE_CREATE_ERROR` / `ACCOUNT_CREATE_ERROR` | Unexpected
        server error while provisioning |


        Note: `traderId`, `email`, and `unassigned` are mutually exclusive —
        send exactly one. Sending several, or none, is a `400 VALIDATION_ERROR`.
        `customerId` is always required (`externalRef` is accepted as a
        deprecated alias).
      operationId: postV1OrganizationTradingaccounts
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Model562'
      responses:
        '201':
          description: Success - Trading account created
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Model569'
        '400':
          description: Validation failed
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Model19'
        '401':
          description: Authentication required
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Model9'
        '403':
          description: Only organization admins can perform this action
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Model570'
        '404':
          description: No Hyperprop user found for the given traderId or email
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Model571'
        '409':
          description: Resource already exists
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Model572'
        '500':
          description: An unexpected error occurred
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Model5'
      security:
        - X-API-Key: []
components:
  schemas:
    Model562:
      type: object
      properties:
        type:
          $ref: '#/components/schemas/Model556'
        tradingPlanId:
          type: string
          description: ID of the trading plan (get from /trading-plans)
          example: 550e8400-e29b-41d4-a716-446655440000
          x-format:
            guid: true
        tradingRuleId:
          type: string
          description: Override default rules (optional)
          example: 660e8400-e29b-41d4-a716-446655440000
          x-format:
            guid: true
        traderId:
          type: string
          description: >-
            Hyperprop user ID to link this account to. Provide EITHER traderId
            OR email (exactly one). Email and name are auto-resolved from the
            user profile.
          example: f994bc02-343c-4026-8a57-afc085eca8d5
          x-format:
            guid: true
        email:
          type: string
          description: >-
            Email of an EXISTING Hyperprop user. Resolved to their user ID
            server-side. Provide EITHER traderId OR email (exactly one). The
            trader must already have a Hyperprop account.
          example: john@example.com
          x-format:
            email: true
        unassigned:
          type: boolean
          description: >-
            Create the account with NO trader attached. The response (and the
            account.created webhook) contains a one-time importKey — hand it to
            your customer, and they redeem it inside the Hyperprop app under
            Trading Accounts → Import. Billing and market-data entitlement start
            at redemption, not at creation. Mutually exclusive with
            traderId/email.
          example: true
          enum:
            - true
        markets:
          $ref: '#/components/schemas/Model558'
        accountNumber:
          type: string
          description: Custom account number (auto-generated if omitted)
          example: ACME-100K-7845
        initialBalance:
          type: number
          description: Starting balance (defaults to plan's account size)
          example: 100000
        customerId:
          type: string
          description: >-
            REQUIRED. Your customer's ID in YOUR system. Queryable via GET
            /trading-accounts?customerId=..., included in every account payload
            and webhook (data.customerId), and the soulbound-identity key: all
            accounts you create for the same customerId must end up on the same
            Hyperprop login (see notes). Use a stable per-customer ID, not a
            per-order ID.
          example: acme-cust-4471
          maxLength: 255
        externalRef:
          type: string
          description: >-
            DEPRECATED alias for customerId — accepted for backward
            compatibility only. Send customerId instead.
          example: acme-cust-4471
          maxLength: 255
        traderExternalId:
          type: string
          description: >-
            Your own reference for this trader. Stored on the trader record,
            queryable via GET /traders?externalUserId=..., and returned as
            trader.externalUserId in account payloads and webhooks.
          example: acme-user-4471
          maxLength: 255
        metadata:
          $ref: '#/components/schemas/Model559'
        purchase:
          $ref: '#/components/schemas/Model561'
      required:
        - type
        - tradingPlanId
    Model569:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: Trading account created successfully
        data:
          $ref: '#/components/schemas/Model568'
    Model19:
      type: object
      properties:
        success:
          type: boolean
          description: Always false on errors
          example: false
        statusCode:
          type: number
          example: 400
        error:
          type: string
          example: Bad Request
        message:
          type: string
          example: Validation failed
        code:
          type: string
          description: Machine-readable error code — switch on this, not on message text
          example: VALIDATION_ERROR
    Model9:
      type: object
      properties:
        success:
          type: boolean
          description: Always false on errors
          example: false
        statusCode:
          type: number
          example: 401
        error:
          type: string
          example: Unauthorized
        message:
          type: string
          example: Authentication required
        code:
          type: string
          description: Machine-readable error code — switch on this, not on message text
          example: UNAUTHORIZED
    Model570:
      type: object
      properties:
        success:
          type: boolean
          description: Always false on errors
          example: false
        statusCode:
          type: number
          example: 403
        error:
          type: string
          example: Forbidden
        message:
          type: string
          example: Only organization admins can perform this action
        code:
          type: string
          description: Machine-readable error code — switch on this, not on message text
          example: NOT_ADMIN
    Model571:
      type: object
      properties:
        success:
          type: boolean
          description: Always false on errors
          example: false
        statusCode:
          type: number
          example: 404
        error:
          type: string
          example: Not Found
        message:
          type: string
          example: No Hyperprop user found for the given traderId or email
        code:
          type: string
          description: Machine-readable error code — switch on this, not on message text
          example: TRADER_NOT_FOUND
    Model572:
      type: object
      properties:
        success:
          type: boolean
          description: Always false on errors
          example: false
        statusCode:
          type: number
          example: 409
        error:
          type: string
          example: Conflict
        message:
          type: string
          example: Resource already exists
        code:
          type: string
          description: Machine-readable error code — switch on this, not on message text
          example: CONFLICT
    Model5:
      type: object
      properties:
        success:
          type: boolean
          description: Always false on errors
          example: false
        statusCode:
          type: number
          example: 500
        error:
          type: string
          example: Internal Server Error
        message:
          type: string
          example: An unexpected error occurred
        code:
          type: string
          description: Machine-readable error code — switch on this, not on message text
          example: INTERNAL_ERROR
    Model556:
      type: string
      description: Account type
      example: evaluation
      enum:
        - evaluation
        - sim_funded
        - competition
    Model558:
      type: array
      description: >-
        Markets this account may trade. Omit for a futures account: the default
        is the full CME Group bundle. CME Group data is one inseparable licence,
        so naming any of CME/CBOT/NYMEX/COMEX grants all four and is billed once
        at the bundle rate. `BINANCE` (BTCUSDT perpetual) is licensed
        separately, is free public data, and is selected on its own — send
        `["BINANCE"]` for a crypto-only account or include it alongside a CME
        market for both. A crypto-only account is not billed for CME market
        data.
      example:
        - CME
        - CBOT
        - NYMEX
        - COMEX
      minItems: 1
      items:
        $ref: '#/components/schemas/Model557'
    Model559:
      type: object
      description: Custom metadata - store any data you need (JSON)
      example:
        referredBy: mike@partner.com
        discountCode: MIKE10
        internalNotes: Priority support requested
    Model561:
      type: object
      description: Purchase/payment details
      properties:
        pricePaid:
          type: number
          description: Amount the trader paid
          example: 499
        currency:
          type: string
          description: Payment currency
          example: USD
        metadata:
          $ref: '#/components/schemas/Model560'
    Model568:
      type: object
      example:
        id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
        accountNumber: ACC-M3X7K9P2
        customerId: acme-cust-4471
        externalRef: acme-cust-4471
        type: evaluation
        status: not_started
        initialBalance: 50000
        currentBalance: 50000
        highWaterMark: 50000
        dailyStartingBalance: 50000
        startedAt: null
        completedAt: null
        violationReason: null
        markets:
          - CME
          - CBOT
          - NYMEX
          - COMEX
        metadata:
          referredBy: mike@partner.com
          discountCode: MIKE10
        userId: f994bc02-343c-4026-8a57-afc085eca8d5
        userEmail: john.smith@email.com
        importKey: null
        tradingPlan:
          id: 09c93da5-f057-4cc5-82da-ff9220292c94
          name: 50K Challenge
          accountSize: 50000
        tradingRule:
          id: 6bec94a1-425b-47be-bb41-a80e3140e2e7
          name: Standard Rules
          drawdownType: static
        trader:
          email: john.smith@email.com
          name: John Smith
        purchase:
          id: f47ac10b-58cc-4372-a567-0e02b2c3d479
          purchaseDate: '2026-02-18T10:00:00.000Z'
          pricePaid: 299
          currency: USD
        createdAt: '2026-02-18T10:00:00.000Z'
        updatedAt: '2026-02-18T10:00:00.000Z'
      properties:
        id:
          type: string
          description: Unique account ID
        accountNumber:
          type: string
          description: Human-readable account number
        customerId:
          type: string
          description: Your customer ID for this account
        externalRef:
          type: string
          description: DEPRECATED alias for customerId (same value)
        type:
          type: string
          description: Account type
        status:
          type: string
          description: Initial status (always not_started)
        initialBalance:
          type: number
          description: Starting balance
        currentBalance:
          type: number
          description: Current balance
        highWaterMark:
          type: number
          description: High water mark
        dailyStartingBalance:
          type: number
          description: Daily starting balance
        startedAt:
          type: string
          description: When trading started (null until first trade)
        completedAt:
          type: string
          description: When completed/failed (null while active)
        violationReason:
          type: string
          description: Reason for failure (null unless failed)
        markets:
          $ref: '#/components/schemas/Model563'
        metadata:
          $ref: '#/components/schemas/Model564'
        userId:
          type: string
          description: Hyperprop user ID (null until the import key is redeemed)
        userEmail:
          type: string
          description: Hyperprop user email (null until the import key is redeemed)
        importKey:
          type: string
          description: >-
            One-time redemption key (unassigned accounts only, null when a
            trader was linked directly). Hand it to your customer; they redeem
            it in the Hyperprop app under Trading Accounts → Import.
          example: HP-7K3QF-9XT2M-4WHRD
        tradingPlan:
          $ref: '#/components/schemas/Model565'
        tradingRule:
          $ref: '#/components/schemas/Model566'
        trader:
          $ref: '#/components/schemas/trader'
        purchase:
          $ref: '#/components/schemas/Model567'
        createdAt:
          type: string
        updatedAt:
          type: string
    Model557:
      type: string
      enum:
        - CME
        - CBOT
        - NYMEX
        - COMEX
        - BINANCE
    Model560:
      type: object
      description: Payment metadata (store payment IDs, promo codes, etc.)
      example:
        stripePaymentId: pi_3ABC123
        campaign: summer-promo
    Model563:
      type: array
      description: 'Always the full CME Group bundle: CME, CBOT, NYMEX, COMEX'
      items:
        type: string
    Model564:
      type: object
      description: Your custom metadata
    Model565:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        accountSize:
          type: number
    Model566:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
    trader:
      type: object
      properties:
        email:
          type: string
        name:
          type: string
    Model567:
      type: object
      properties:
        id:
          type: string
        purchaseDate:
          type: string
        pricePaid:
          type: number
        currency:
          type: string
  securitySchemes:
    X-API-Key:
      type: apiKey
      name: X-API-Key
      in: header
      description: >-
        Organization API key. Format: "hp_live_{key}". Organization admins
        manage the key in the dashboard.

````

## Related topics

- [Quickstart](/quickstart.md)
- [MCP connector (AI agents)](/guides/mcp-connector.md)
- [Create a new trading plan](/platform-api/trading-plans/create-a-new-trading-plan.md)
- [Create a new trading rule](/platform-api/trading-rules/create-a-new-trading-rule.md)
- [Create or update a copy trading config](/trade-api/copy-trading/create-or-update-a-copy-trading-config.md)


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