REST · JSONv1.0.0

Paper Trade API

Paper Trade maintains one simulated Brokerage Account per Wealth Manager Investor and exposes on-demand Tradable Security data.

Base URL
# Base URL
https://api.papertrade.example

# Every business operation is authenticated
Authorization: Bearer <credential>

Introduction

Paper Trade is a private, API-only service that maintains exactly one simulated Brokerage Account per Wealth Manager Investor and exposes on-demand Tradable Security data.

Every business operation is authenticated with an opaque shared service credential. The reference below documents each resource, its supported methods, request shapes, and the full set of success and error responses.

All monetary fields ending in Cents are integer cents. Share quantities are positive whole shares. Request values are restricted to JavaScript safe integers where stated.

Authentication

All business operations require the opaque shared service credential passed in an Authorization: Bearer <credential> header. The credential is not a JWT — it is an opaque shared secret configured server-side as PAPER_TRADE_SERVICE_CREDENTIAL.

OPTIONS and unsupported-method responses do not authenticate. A missing, malformed, or incorrect credential returns 401 unauthorized.

Keep the credential server-side. It grants full access to every Investor account and should never be exposed to browser code.

Authorization header
Authorization: Bearer <credential>

Idempotency

Every account mutation requires an Idempotency-Key header. Keys are scoped to the exact Investor and retained with terminal results.

Repeating the same key with a normalized-identical request replays the original status and body. Reusing the same key for a different request returns 409 idempotency_conflict.

Additional properties on mutation bodies are accepted, ignored, and excluded from the idempotency fingerprint.

Idempotency header
Idempotency-Key: deposit-investor-123-1

Method handling

Documented HEAD operations are provided by Next.js by invoking GET and suppressing the body.

Unsupported exported methods return 405 with {"error":{"code":"method_not_allowed","message":"Method not allowed."}} and the same Allow value shown by the path’s OPTIONS operation. These rejecting methods are intentionally omitted as OpenAPI operations.

OPTIONS responses expose Allow only; they are not complete CORS preflight responses and emit no Access-Control-Allow-* headers.

405 response body
{
  "error": {
    "code": "method_not_allowed",
    "message": "Method not allowed."
  }
}

Brokerage Accounts

Brokerage Account lifecycle, balances, Positions, and Account Activity.

GET/api/investors/{investorId}/account

Get a Brokerage Account

Returns durable cash, cumulative Realized Gain or Loss, and Positions. No market data is requested. Positions are ordered by Ticker ascending.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 200Brokerage Account found.
    200 response
    {
      "investorId": "investor-123",
      "availableCashCents": 1000000,
      "realizedGainLossCents": 0,
      "positions": []
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 404No Brokerage Account exists for the exact Investor ID.
    404 response
    {
      "error": {
        "code": "not_found",
        "message": "Brokerage Account not found."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
Request
curl -X GET "https://api.papertrade.example/api/investors/investor-123/account" \
  -H "Authorization: Bearer <credential>"
200 response
{
  "investorId": "investor-123",
  "availableCashCents": 1000000,
  "realizedGainLossCents": 0,
  "positions": []
}
POST/api/investors/{investorId}/account

Create a Brokerage Account

Creates the account and its starting_cash Account Activity atomically. One account is allowed per exact Investor ID. An exact idempotent replay returns the original 201 body and does not create another activity.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

  • Idempotency-Keyheaderstringrequired

    Investor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.

Request body

Starting cash for the new account, in integer cents.

Responses

  • 201Account created, or the original successful result replayed.
    201 response
    {
      "investorId": "investor-123",
      "availableCashCents": 1000000,
      "realizedGainLossCents": 0,
      "positions": []
    }
  • 400Body or Idempotency-Key is missing, malformed, or invalid.
    400 response
    {
      "error": {
        "code": "invalid_request",
        "message": "startingCashCents must be a non-negative safe integer and Idempotency-Key is required."
      }
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 409The idempotency key conflicts, or the Investor already has an account.
    409 response
    {
      "error": {
        "code": "account_already_exists",
        "message": "A Brokerage Account already exists for this Investor."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
Request
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account" \
  -H "Authorization: Bearer <credential>" \
  -H "Idempotency-Key: deposit-investor-123-1" \
  -H "Content-Type: application/json" \
  -d '{ "startingCashCents": 1000000 }'
Body · Standard
{
  "startingCashCents": 1000000
}
201 response
{
  "investorId": "investor-123",
  "availableCashCents": 1000000,
  "realizedGainLossCents": 0,
  "positions": []
}
HEAD/api/investors/{investorId}/account

Check for a Brokerage Account

Runs the GET operation, including authentication and database reads, but returns no body.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 200Brokerage Account found; body omitted.
  • 401Bearer credential is missing or invalid; body omitted.
  • 404Brokerage Account not found; body omitted.
  • 500The request could not be completed; body omitted.
Request
curl -X HEAD "https://api.papertrade.example/api/investors/investor-123/account" \
  -H "Authorization: Bearer <credential>"
OPTIONS/api/investors/{investorId}/accountNo auth

List supported account methods

Returns Allow: GET, HEAD, OPTIONS, POST. No body and no authentication.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS, POST.
Request
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account"
POST/api/investors/{investorId}/account/deposits

Deposit cash

Adds cash and records one cash_deposit Account Activity atomically. A successful exact replay returns the original account snapshot without moving cash again. A terminal 404 or 422 is also retained for replay. Allow: OPTIONS, POST.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

  • Idempotency-Keyheaderstringrequired

    Investor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.

Request body

Cash amount to deposit, in positive integer cents.

Responses

  • 200Deposit committed, or the original successful result replayed.
    200 response
    {
      "investorId": "investor-123",
      "availableCashCents": 1025000,
      "realizedGainLossCents": 0,
      "positions": []
    }
  • 400Body, amount, or Idempotency-Key is missing, malformed, or invalid.
    400 response
    {
      "error": {
        "code": "invalid_request",
        "message": "amountCents must be a positive safe integer and Idempotency-Key is required."
      }
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 404No Brokerage Account exists for the exact Investor ID.
    404 response
    {
      "error": {
        "code": "not_found",
        "message": "Brokerage Account not found."
      }
    }
  • 409The Investor-scoped key was already used with a different normalized request.
    409 response
    {
      "error": {
        "code": "idempotency_conflict",
        "message": "Idempotency key was already used with a different request."
      }
    }
  • 422The deposit would make Available Cash exceed the supported safe-integer limit.
    422 response
    {
      "error": {
        "code": "cash_limit_exceeded",
        "message": "Cash Deposit would exceed the supported cash limit."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
Request
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account/deposits" \
  -H "Authorization: Bearer <credential>" \
  -H "Idempotency-Key: deposit-investor-123-1" \
  -H "Content-Type: application/json" \
  -d '{ "amountCents": 25000 }'
Request body
{
  "amountCents": 25000
}
200 response
{
  "investorId": "investor-123",
  "availableCashCents": 1025000,
  "realizedGainLossCents": 0,
  "positions": []
}
OPTIONS/api/investors/{investorId}/account/depositsNo auth

List supported deposit methods

Returns Allow: OPTIONS, POST. No body and no authentication.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 204Supported methods returned; no response body. Allow: OPTIONS, POST.
Request
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/deposits"
POST/api/investors/{investorId}/account/withdrawals

Withdraw cash

Subtracts cash and records one cash_withdrawal Account Activity atomically. Withdrawing the entire Available Cash balance is allowed. Concurrent withdrawals are serialized and cannot overdraw the account. Allow: OPTIONS, POST.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

  • Idempotency-Keyheaderstringrequired

    Investor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.

Request body

Cash amount to withdraw, in positive integer cents.

Responses

  • 200Withdrawal committed, or the original successful result replayed.
    200 response
    {
      "investorId": "investor-123",
      "availableCashCents": 970000,
      "realizedGainLossCents": 0,
      "positions": []
    }
  • 400Body, amount, or Idempotency-Key is missing, malformed, or invalid.
    400 response
    {
      "error": {
        "code": "invalid_request",
        "message": "amountCents must be a positive safe integer and Idempotency-Key is required."
      }
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 404No Brokerage Account exists for the exact Investor ID.
    404 response
    {
      "error": {
        "code": "not_found",
        "message": "Brokerage Account not found."
      }
    }
  • 409The Investor-scoped key was already used with a different normalized request.
    409 response
    {
      "error": {
        "code": "idempotency_conflict",
        "message": "Idempotency key was already used with a different request."
      }
    }
  • 422The withdrawal exceeds currently locked Available Cash.
    422 response
    {
      "error": {
        "code": "insufficient_cash",
        "message": "Cash Withdrawal exceeds Available Cash."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
Request
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account/withdrawals" \
  -H "Authorization: Bearer <credential>" \
  -H "Idempotency-Key: deposit-investor-123-1" \
  -H "Content-Type: application/json" \
  -d '{ "amountCents": 25000 }'
Request body
{
  "amountCents": 25000
}
200 response
{
  "investorId": "investor-123",
  "availableCashCents": 970000,
  "realizedGainLossCents": 0,
  "positions": []
}
OPTIONS/api/investors/{investorId}/account/withdrawalsNo auth

List supported withdrawal methods

Returns Allow: OPTIONS, POST. No body and no authentication.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 204Supported methods returned; no response body. Allow: OPTIONS, POST.
Request
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/withdrawals"
GET/api/investors/{investorId}/account/activities

List Account Activity

Returns at most 100 activities, newest first. Equal timestamps are ordered by the internal activity identifier descending. There is no pagination, filtering, or caller-selectable limit. Allow: GET, HEAD, OPTIONS.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 200Account exists; its activity history is returned.
    200 response
    {
      "activities": [
        {
          "type": "cash_withdrawal",
          "amountCents": 30000,
          "createdAt": "2026-07-13T12:02:00.000Z"
        },
        {
          "type": "cash_deposit",
          "amountCents": 25000,
          "createdAt": "2026-07-13T12:01:00.000Z"
        },
        {
          "type": "starting_cash",
          "amountCents": 1000000,
          "createdAt": "2026-07-13T12:00:00.000Z"
        }
      ]
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 404No Brokerage Account exists for the exact Investor ID.
    404 response
    {
      "error": {
        "code": "not_found",
        "message": "Brokerage Account not found."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
Request
curl -X GET "https://api.papertrade.example/api/investors/investor-123/account/activities" \
  -H "Authorization: Bearer <credential>"
200 response
{
  "activities": [
    {
      "type": "cash_withdrawal",
      "amountCents": 30000,
      "createdAt": "2026-07-13T12:02:00.000Z"
    },
    {
      "type": "cash_deposit",
      "amountCents": 25000,
      "createdAt": "2026-07-13T12:01:00.000Z"
    },
    {
      "type": "starting_cash",
      "amountCents": 1000000,
      "createdAt": "2026-07-13T12:00:00.000Z"
    }
  ]
}
HEAD/api/investors/{investorId}/account/activities

Check Account Activity availability

Runs the GET operation, including database reads, but returns no body.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 200Account exists; body omitted.
  • 401Bearer credential is missing or invalid; body omitted.
  • 404Brokerage Account not found; body omitted.
  • 500The request could not be completed; body omitted.
Request
curl -X HEAD "https://api.papertrade.example/api/investors/investor-123/account/activities" \
  -H "Authorization: Bearer <credential>"
OPTIONS/api/investors/{investorId}/account/activitiesNo auth

List supported activity methods

Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
Request
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/activities"

Market Orders

Synchronous simulated Buy and Sell fills.

POST/api/investors/{investorId}/account/market-orders

Execute a Buy or Sell Market Order

Executes synchronously against a freshly fetched quote and returns an immutable Fill; no Market Order resource is persisted. Tickers are trimmed and uppercased before validation. The simplified session is Monday–Friday, 09:30 inclusive to 16:00 exclusive in America/New_York; holidays and early closes are ignored. Facts and quote data are fetched before account lookup, so an unknown Investor can receive market-session, security, or provider errors before 404. Matching idempotent replays occur before those checks. Allow: OPTIONS, POST.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

  • Idempotency-Keyheaderstringrequired

    Investor-scoped mutation key containing at least one non-whitespace character. The original value is retained as the key; no maximum length is enforced.

Request body

Order side, ticker (trimmed and uppercased before validation), and positive whole-share quantity.

Responses

  • 200The order filled, or the original Fill was replayed.
    200 response
    {
      "type": "buy_fill",
      "ticker": "AAPL",
      "quantity": 2,
      "priceCents": 21134,
      "totalCents": 42268,
      "quoteTimestamp": "2026-07-13T14:30:00.000Z"
    }
  • 400Body, order fields, or Idempotency-Key is missing, malformed, or invalid.
    400 response
    {
      "error": {
        "code": "invalid_request",
        "message": "side must be \"buy\" or \"sell\", Ticker must be valid, quantity must be a positive safe whole number, and Idempotency-Key is required."
      }
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 404No Brokerage Account exists for the exact Investor ID.
    404 response
    {
      "error": {
        "code": "not_found",
        "message": "Brokerage Account not found."
      }
    }
  • 409The Investor-scoped key was already used with a different normalized request.
    409 response
    {
      "error": {
        "code": "idempotency_conflict",
        "message": "Idempotency key was already used with a different request."
      }
    }
  • 422A terminal domain rule rejected the order: market_closed, unsupported_ticker, insufficient_cash, position_limit_exceeded, insufficient_shares, or account_limit_exceeded.
    422 response
    {
      "error": {
        "code": "insufficient_cash",
        "message": "Buy Market Order exceeds Available Cash."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
  • 503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
    503 response
    {
      "error": {
        "code": "market_data_unavailable",
        "message": "Market data is temporarily unavailable."
      }
    }
Request
curl -X POST "https://api.papertrade.example/api/investors/investor-123/account/market-orders" \
  -H "Authorization: Bearer <credential>" \
  -H "Idempotency-Key: deposit-investor-123-1" \
  -H "Content-Type: application/json" \
  -d '{ "side": "buy", "ticker": " aapl ", "quantity": 2 }'
Body · Buy
{
  "side": "buy",
  "ticker": " aapl ",
  "quantity": 2
}
200 response
{
  "type": "buy_fill",
  "ticker": "AAPL",
  "quantity": 2,
  "priceCents": 21134,
  "totalCents": 42268,
  "quoteTimestamp": "2026-07-13T14:30:00.000Z"
}
OPTIONS/api/investors/{investorId}/account/market-ordersNo auth

List supported Market Order methods

Returns Allow: OPTIONS, POST. No body and no authentication.

Parameters

  • investorIdpathstringrequired

    Exact, opaque Investor identifier. The application does not trim, normalize, length-limit, or pattern-check this value.

Responses

  • 204Supported methods returned; no response body. Allow: OPTIONS, POST.
Request
curl -X OPTIONS "https://api.papertrade.example/api/investors/investor-123/account/market-orders"

Tradable Securities

On-demand security facts, quotes, and daily price history.

GET/api/securities/{ticker}

Look up a Tradable Security

Performs an exact lookup after trimming and uppercasing the Ticker. There is no fuzzy search or alias matching. The provider request is not cached. Returned name and exchange are trimmed; exchange casing is provider-controlled. Allow: GET, HEAD, OPTIONS.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 200An active security on an accepted US exchange was found.
    200 response
    {
      "ticker": "AAPL",
      "name": "Apple Inc.",
      "exchange": "NASDAQ"
    }
  • 400Ticker is malformed after trimming and uppercasing.
    400 response
    {
      "error": {
        "code": "invalid_request",
        "message": "Ticker is malformed."
      }
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 422Provider returned not-found, or facts identify an inactive security or a listing outside the accepted exchange set.
    422 response
    {
      "error": {
        "code": "unsupported_ticker",
        "message": "Ticker is not a supported active US-listed stock or ETF."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
  • 503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
    503 response
    {
      "error": {
        "code": "market_data_unavailable",
        "message": "Market data is temporarily unavailable."
      }
    }
Request
curl -X GET "https://api.papertrade.example/api/securities/AAPL" \
  -H "Authorization: Bearer <credential>"
200 response
{
  "ticker": "AAPL",
  "name": "Apple Inc.",
  "exchange": "NASDAQ"
}
HEAD/api/securities/{ticker}

Check Tradable Security availability

Runs the GET operation, including its provider request, but returns no body.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 200Tradable Security found; body omitted.
  • 400Ticker is malformed; body omitted.
  • 401Bearer credential is missing or invalid; body omitted.
  • 422Ticker is unsupported; body omitted.
  • 500The request could not be completed; body omitted.
  • 503Market data is unavailable; body omitted.
Request
curl -X HEAD "https://api.papertrade.example/api/securities/AAPL" \
  -H "Authorization: Bearer <credential>"
OPTIONS/api/securities/{ticker}No auth

List supported security lookup methods

Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
Request
curl -X OPTIONS "https://api.papertrade.example/api/securities/AAPL"
GET/api/securities/{ticker}/quote

Get a current quote

Fetches a non-cached provider snapshot. Price is rounded to the nearest cent. This endpoint does not fetch company facts, so it does not itself verify active status, exchange, or asset class. Allow: GET, HEAD, OPTIONS.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 200A usable quote was returned by the provider.
    200 response
    {
      "ticker": "AAPL",
      "priceCents": 21134,
      "quoteTimestamp": "2026-07-13T14:30:00.000Z"
    }
  • 400Ticker is malformed after trimming and uppercasing.
    400 response
    {
      "error": {
        "code": "invalid_request",
        "message": "Ticker is malformed."
      }
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 422Provider returned not-found, or facts identify an inactive security or a listing outside the accepted exchange set.
    422 response
    {
      "error": {
        "code": "unsupported_ticker",
        "message": "Ticker is not a supported active US-listed stock or ETF."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
  • 503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
    503 response
    {
      "error": {
        "code": "market_data_unavailable",
        "message": "Market data is temporarily unavailable."
      }
    }
Request
curl -X GET "https://api.papertrade.example/api/securities/AAPL/quote" \
  -H "Authorization: Bearer <credential>"
200 response
{
  "ticker": "AAPL",
  "priceCents": 21134,
  "quoteTimestamp": "2026-07-13T14:30:00.000Z"
}
HEAD/api/securities/{ticker}/quote

Check quote availability

Runs the GET operation, including its provider request, but returns no body.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 200Quote available; body omitted.
  • 400Ticker is malformed; body omitted.
  • 401Bearer credential is missing or invalid; body omitted.
  • 422Ticker is unsupported; body omitted.
  • 500The request could not be completed; body omitted.
  • 503Market data is unavailable; body omitted.
Request
curl -X HEAD "https://api.papertrade.example/api/securities/AAPL/quote" \
  -H "Authorization: Bearer <credential>"
OPTIONS/api/securities/{ticker}/quoteNo auth

List supported quote methods

Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
Request
curl -X OPTIONS "https://api.papertrade.example/api/securities/AAPL/quote"
GET/api/securities/{ticker}/prices

Get daily historical prices

Returns provider daily OHLCV rows in the requested inclusive date range. startDate must be no later than endDate. Rows are not sorted, deduplicated, synthesized, or filled; provider order is preserved and an empty array is valid. One malformed or out-of-range provider row rejects the entire response as 503. Allow: GET, HEAD, OPTIONS.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

  • startDatequerystring (date)required

    Inclusive first calendar date. Must be no later than endDate.

  • endDatequerystring (date)required

    Inclusive last calendar date. Must be no earlier than startDate.

Responses

  • 200Historical rows returned; the list may be empty.
    200 response
    {
      "ticker": "AAPL",
      "prices": [
        {
          "date": "2026-01-02",
          "openCents": 24385,
          "highCents": 24415,
          "lowCents": 24191,
          "closeCents": 24336,
          "volume": 40230800
        }
      ]
    }
  • 400Ticker or inclusive date range is invalid.
    400 response
    {
      "error": {
        "code": "invalid_request",
        "message": "startDate and endDate must be valid YYYY-MM-DD dates in chronological order."
      }
    }
  • 401Bearer credential is missing, malformed, or incorrect.
    401 response
    {
      "error": {
        "code": "unauthorized",
        "message": "A valid bearer credential is required."
      }
    }
  • 422Provider returned not-found, or facts identify an inactive security or a listing outside the accepted exchange set.
    422 response
    {
      "error": {
        "code": "unsupported_ticker",
        "message": "Ticker is not a supported active US-listed stock or ETF."
      }
    }
  • 500The route caught an unexpected failure, including missing credential configuration and database failures.
    500 response
    {
      "error": {
        "code": "internal_error",
        "message": "The request could not be completed."
      }
    }
  • 503Provider credentials, timeout, transport, status, JSON, or returned data were unavailable or unusable. Provider details are not exposed.
    503 response
    {
      "error": {
        "code": "market_data_unavailable",
        "message": "Market data is temporarily unavailable."
      }
    }
Request
curl -X GET "https://api.papertrade.example/api/securities/AAPL/prices?startDate=2026-01-02&endDate=2026-01-05" \
  -H "Authorization: Bearer <credential>"
200 response
{
  "ticker": "AAPL",
  "prices": [
    {
      "date": "2026-01-02",
      "openCents": 24385,
      "highCents": 24415,
      "lowCents": 24191,
      "closeCents": 24336,
      "volume": 40230800
    }
  ]
}
HEAD/api/securities/{ticker}/prices

Check historical-price availability

Runs the GET operation, including provider requests, but returns no body.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 200Historical-price request succeeded; body omitted.
  • 400Ticker or date range is invalid; body omitted.
  • 401Bearer credential is missing or invalid; body omitted.
  • 422Ticker is unsupported; body omitted.
  • 500The request could not be completed; body omitted.
  • 503Market data is unavailable; body omitted.
Request
curl -X HEAD "https://api.papertrade.example/api/securities/AAPL/prices" \
  -H "Authorization: Bearer <credential>"
OPTIONS/api/securities/{ticker}/pricesNo auth

List supported historical-price methods

Returns Allow: GET, HEAD, OPTIONS. No body and no authentication.

Parameters

  • tickerpathstringrequired

    Ticker input. Trimmed and uppercased, then required to be 1–10 ASCII characters matching ^[A-Z][A-Z0-9.-]{0,9}$. Lowercase and surrounding whitespace are accepted.

Responses

  • 204Supported methods returned; no response body. Allow: GET, HEAD, OPTIONS.
Request
curl -X OPTIONS "https://api.papertrade.example/api/securities/AAPL/prices"

Data models

The response shapes returned across the API. Monetary values ending in Cents are integer cents; safe integers stay within JavaScript's safe range.

BrokerageAccount

Durable state of an Investor’s simulated brokerage account.

  • investorIdstring

    Exact, opaque Investor identifier.

  • availableCashCentsinteger ≥ 0

    Available Cash in cents.

  • realizedGainLossCentsinteger

    Cumulative signed Realized Gain or Loss in cents.

  • positionsPosition[]

    Positions ordered by Ticker ascending.

Position

A single held security within a Brokerage Account.

  • tickerCanonicalTicker

    ^[A-Z][A-Z0-9.-]{0,9}$, 1–10 chars.

  • quantityinteger ≥ 1

    Whole shares.

  • averageCostBasisCentsinteger ≥ 0

    Rounded weighted-average cost basis in cents per share.

MarketOrderFill

Immutable result of a filled Market Order. Discriminated by type into BuyFill and SellFill.

BuyFill (type: buy_fill)SellFill (type: sell_fill)
  • type"buy_fill" | "sell_fill"

    Discriminator.

  • tickerCanonicalTicker

    Filled security.

  • quantityinteger ≥ 1

    Whole shares filled.

  • priceCentsinteger ≥ 1

    Fill price per share in cents.

  • totalCentsinteger ≥ 1

    Total consideration in cents.

  • costBasisCentsinteger ≥ 0optional

    Sell only: basis released by the sale.

  • realizedGainLossCentsintegeroptional

    Sell only: signed realized gain or loss.

  • quoteTimestampstring (date-time)

    Provider quote timestamp (UTC ISO 8601).

AccountActivity

One entry in an account’s history. Discriminated by type.

starting_cashcash_depositcash_withdrawalbuy_fillsell_fill
  • typeactivity type

    Discriminator for the activity kind.

  • amountCentsintegeroptional

    Cash activities: amount moved, in cents.

  • tickerCanonicalTickeroptional

    Fill activities: filled security.

  • createdAtstring (date-time)

    When the activity was recorded (UTC ISO 8601).

List responses return at most 100 activities, newest first.

TradableSecurity

Company facts for an active, US-listed security.

  • tickerCanonicalTicker

    Canonical uppercase ticker.

  • namestring

    Trimmed provider name; at least one non-whitespace char.

  • exchangestring

    One of AMEX, ARCA, BATS, CBOE, CBOE BZX, NASDAQ, NYSE, NYSE AMERICAN, NYSE ARCA. Provider casing preserved.

SecurityQuote

A single point-in-time provider quote.

  • tickerCanonicalTicker

    Canonical uppercase ticker.

  • priceCentsinteger ≥ 1

    Quote price rounded to the nearest cent.

  • quoteTimestampstring (date-time)

    Provider source timestamp normalized to UTC ISO 8601.

DailyPrice

A single daily OHLCV row. High is at least every OHLC value; low is at most every OHLC value.

  • datestring (date)

    Trading day (YYYY-MM-DD).

  • openCentsinteger ≥ 1

    Opening price in cents.

  • highCentsinteger ≥ 1

    High price in cents.

  • lowCentsinteger ≥ 1

    Low price in cents.

  • closeCentsinteger ≥ 1

    Closing price in cents.

  • volumeinteger ≥ 0 | null

    Provider-supplied daily volume, or null when unavailable.

Error codes

Errors are returned as { error: { code, message } } with a stable machine-readable code.

CodeStatus
unauthorized

A valid bearer credential is required.

401
invalid_request

Body, ticker, dates, or Idempotency-Key is malformed.

400
not_found

Brokerage Account not found.

404
account_already_exists

A Brokerage Account already exists for this Investor.

409
idempotency_conflict

Idempotency key was already used with a different request.

409
cash_limit_exceeded

Cash Deposit would exceed the supported cash limit.

422
insufficient_cash

Cash Withdrawal / Buy exceeds Available Cash.

422
market_closed

Market Orders are accepted only during the Paper Trade session.

422
unsupported_ticker

Ticker is not a supported active US-listed stock or ETF.

422
position_limit_exceeded

Buy Market Order would exceed the supported Position limit.

422
insufficient_shares

Sell Market Order exceeds the Position quantity.

422
account_limit_exceeded

Sell Market Order would exceed the supported account limit.

422
internal_error

The request could not be completed.

500
market_data_unavailable

Market data is temporarily unavailable.

503
method_not_allowed

Method not allowed.

405