Skip to content

Backend API Documentation

Base URL

Development Environment: https://api.dev.skyll.space Production Environment: https://api.skyll.space

API Documentation

Interactive API documentation is available at: {baseUrl}/swagger Note: All endpoint details and query parameters are specified in the Swagger documentation. You can find the URL here: api.dev.skyll.space/swagger

Authentication

JWT Authentication

The API uses JWT (JSON Web Token) authentication. All authenticated requests should include the JWT token in the Authorization header:

Authorization: Bearer {access_token}

Token Lifecycle

  • Access Token Expiration: 24 hours
  • Refresh Token Expiration: 7 days
  • ! Tokens must be removed from browser storage upon logout

Rate Limiting

  • User throttle limit: 10,000 requests per day
  • Applies to all authenticated endpoints

Authentication Flow

  1. Registration

    • Endpoint: POST /api/v1/auth/registration/
    • Required fields: username, email, password
    • Email verification required before login
  2. Email Verification

    • User receives verification email
    • Frontend implements confirmation page that accepts the key parameter
    • Frontend must then call: POST /api/v1/auth/registration/verify-email/ with the key parameter
  3. Login

    • Endpoint: POST /api/v1/auth/login/
    • Required fields: email, password
    • Response includes access_token and refresh_token
  4. Password Reset

    • Request: POST /api/v1/auth/password/reset/
    • User receives email with reset link
    • Frontend will need to implement password reset page that accepts uid and token parameters
    • Frontend must then call: POST /api/v1/auth/password/reset/confirm/
  5. Logout

    • Endpoint: POST /api/v1/auth/token/revoke/
    • Required field: refresh (the refresh token to revoke)
    • Frontend must also remove all tokens from storage
    • This prevents refresh tokens from being used after logout

User Types and Permissions

We currently have two separate concepts in our system: traders and users.

Traders

  • We fetch traders and their trades from the trading provider via their API.
  • The client manages their trading group within the trading provider platform and gave us an API token for access.
  • Whenever the API returns a new trader that doesn’t exist in our database, we automatically create a new Trader object for them. This process is fully automated.
  • Traders are identified by their platform account ids

Users

  • Users are separate from traders — these are the accounts you log in with (like your fe_trader or fe_admin test accounts).
  • Users are not created automatically. An admin needs to manually create a user and link it to the corresponding Trader object.
  • This link is necessary so that when a user logs in, we know which trader’s data to display for them.

We have two types of users.

  • Admin users are able to see other traders analytics using the: Coach Individual View (see Study/Apps, Coach Individual View.
  • Admin users see real profit, not just the obfuscated one for charts where obfuscation is applied

Regular User

  • Linked to a Trader account (manual admin setup required)
  • Access to personal analytics with real PnL numbers
  • Access to comparative analytics within their group. Some comparative endpoints return real USD values, while others remain obfuscated.

Admin User

  • Can access real data for all traders within their group
  • May or may not have a linked trading account
  • Can access personal analytics for any trader in their group using trader query parameter
  • Empty personal analytics if no trading account is linked

API Structure

Personal Analytics

  • Base path: /api/v1/personal/*
  • Returns individual trader data
  • Admin users can query other traders using trader parameter
  • Includes tag analytics via GET api/v1/personal/performance-by-tag/

Comparative Analytics

  • Base path: /api/v1/comparative/*
  • Returns group-level data
  • Data visibility based on user role (real vs. obfuscated)
  • Includes tag analytics via GET api/v1/comparative/performance-by-tag/

Date/Time Handling

  • All timestamps in responses are in UTC
  • Date parameters in requests should use format: YYYY-MM-DD
  • Time-based query parameters are specified in the Swagger documentation for each endpoint

Response Format

Success Response

{
    "data": {
        // Response data
    },
    "meta": {
        // Metadata about the response
    }
}

Error Response

{
    "error": "Error message"
}

Important Notes

  • All timestamps are in UTC
  • API endpoints should not include trailing slashes
  • Analytics endpoints have caches with different cache times
  • Test accounts available for Dev environment:

    • Admin • Can see all data in comparative (no obfuscation, real USD) • Can view any trader’s personal analytics ** • Can see opt-out traders** in comparative

      • Data are delayed by one day on many charts, so if you see No Data Available, its correct
      • Username: test_admin@mail.com
      • Password: reach out to Admin for password
    • Trader

      • Can see **their own personal analytics **
      • Can see most comparative data (some charts obfuscated, some charts show real USD profits)
      • Cannot see opt-out traders in comparative
      • Username: test_trader@mail.com
      • Password: reach out to Admin for password
    • Trader Opt-Out • Can see their own personal analytics ** • Cannot see any comparative data **

Testing in Swagger

  1. Login using provided credentials using the login endpoint
  2. Copy the access token
  3. Click 'Authorize' button
  4. Enter token as: Bearer {access_token}

CHANGELOG

3.4.2026

Database Change: Login history is now stored in user_management_audit_log

  • Successful logins are now written to the shared database table user_management_audit_log.
  • This is a database-level change for backend developers and downstream consumers. It is not a new public API endpoint.
  • Login events share the table with User Management audit rows, so consumers must filter by the logical dataset.

How to Read Login Data

  • Query user_management_audit_log
  • Filter with:
    • scope = 'authentication'
    • action = 'login'
    • optionally entity_type = 'custom_user'
  • Use these fields:
    • timestamp = when the login happened
    • user_email = which Skyll user logged in
    • entity_id = CustomUser.id
    • trader_id_snapshot = trader assigned to that user at login time
    • changes->source->>new = api or console

Important Notes

  • trader_id_snapshot is a snapshot at login time. Historical rows do not change if the user is reassigned later.
  • User Management audit rows remain in the same table with scope = 'user_management'.
  • If you only filter by action = 'login', you will currently get login rows, but downstream readers should still filter by scope = 'authentication' as the primary discriminator.

Example Query

SELECT
    timestamp,
    user_email,
    entity_id AS custom_user_id,
    trader_id_snapshot,
    changes->'source'->>'new' AS source
FROM user_management_audit_log
WHERE scope = 'authentication'
  AND action = 'login'
ORDER BY timestamp DESC;

27.3.2026

Update: intraday-daily-pnl now includes fill-based daily close

  • Updated GET api/v1/personal/intraday-daily-pnl/.
  • Every candle in both data.Individual[].candles[] and data.Aggregated.candles[] now includes close_pnl_from_fills.
  • close_pnl_from_fills is the closing PnL for that day calculated from trades/fills. It is not derived from the intraday candle table.
  • Use close_pnl_from_fills as the day-end result when close_pnl looks incomplete because some products are still missing intraday price data.
  • Existing OHLC fields (open_pnl, high_pnl, low_pnl, close_pnl) still come from the intraday daily PnL table and were not changed.
  • If a product or day has no intraday daily candle yet, but there is trade/fill PnL for it, the API now returns a synthetic candle with:
    • open_pnl = 0
    • high_pnl = 0
    • low_pnl = 0
    • close_pnl = 0
    • total_trades = 0
    • close_pnl_from_fills = <actual day close from trades/fills>

20.3.2026

New Feature: Tags Analytics Endpoints

  • Added two new tag analytics endpoints for Trading Journal analytics.
  • Both endpoints aggregate tagged trades by the fixed tag hierarchy: Layer 1 category -> Layer 2 tag.
  • Personal analytics also include trader-specific custom tags as a third level in the hierarchy.
  • Comparative analytics do not include custom tags. Only fixed tags are returned there.

API Changes

  • GET api/v1/personal/performance-by-tag/

    • Returns personal tag analytics for the authenticated trader.
    • Admin users can query another trader by adding the trader query parameter.
    • Query Parameters:
      • start_date (string, YYYY-MM-DD, required): Inclusive UTC start date
      • end_date (string, YYYY-MM-DD, required): Inclusive UTC end date
      • trader (integer, optional): Admin-only trader ID override
    • Response shape:
      • Top-level data array of Layer 1 categories
      • Each category has id, type, name, pnl, trade_count, children
      • Layer 2 tags are returned in children
      • Custom tags are returned as children of their Layer 2 parent tag
    • Returns real USD PnL
  • GET api/v1/comparative/performance-by-tag/

    • Returns comparative tag analytics for the authenticated user's group.
    • The aggregate includes the requesting trader's visible accounts as well. It is not a peer-only dataset.
    • Query Parameters:
      • start_date (string, YYYY-MM-DD, required): Inclusive UTC start date
      • end_date (string, YYYY-MM-DD, required): Inclusive UTC end date
    • Response shape:
      • Same top-level hierarchy model as the personal endpoint
      • Only fixed tags are returned: Layer 1 -> Layer 2
      • No custom tags are included
    • Returns real USD PnL for all users
    • Uses the existing comparative visibility rules for which group accounts are included:
      • Non-admins do not see opted-out traders
      • Admins can see opted-out traders
      • Sim-account exclusion rules remain unchanged

Example Response Structure

Personal:

{
  "data": [
    {
      "id": 1,
      "type": "fixed",
      "name": "Tech",
      "pnl": 1250.5,
      "trade_count": 14,
      "children": [
        {
          "id": 10,
          "type": "fixed",
          "name": "Other",
          "pnl": 300.0,
          "trade_count": 4,
          "children": [
            {
              "id": 45,
              "type": "custom",
              "name": "morning-session",
              "pnl": 180.0,
              "trade_count": 2,
              "children": []
            }
          ]
        }
      ]
    }
  ],
  "meta": {
    "start_date": "2026-03-01",
    "end_date": "2026-03-31",
    "trader": 123,
    "total_categories": 5,
    "total_subcategories": 30,
    "total_custom_tags": 8
  }
}

Comparative:

{
  "data": [
    {
      "id": 1,
      "type": "fixed",
      "name": "Tech",
      "pnl": 2400.0,
      "trade_count": 23,
      "children": [
        {
          "id": 10,
          "type": "fixed",
          "name": "Other",
          "pnl": 400.0,
          "trade_count": 5,
          "children": []
        }
      ]
    }
  ],
  "meta": {
    "start_date": "2026-03-01",
    "end_date": "2026-03-31",
    "total_categories": 5,
    "total_subcategories": 30,
    "obfuscated": false
  }
}

Frontend Implementation Guide

  • The backend returns the full fixed tag taxonomy in stable order, including zero-value rows. Use that directly for row alignment and display.
  • Period presets such as Day, Week, Month, All, or Custom should be handled on the frontend by converting the selected preset into start_date and end_date values before calling the endpoint.
  • Display modes such as All Levels, First Level Only, Making Money, and Losing Money are expected to be handled on the frontend. The backend intentionally returns the full hierarchy so the frontend can filter or collapse rows client-side without extra API calls.
  • Use pnl as the signed realized USD PnL value and trade_count as the number of tagged trades.

16.2.2026

Database Schema Update: New Trader and Group Fields

  • Added new database fields documented for API consumers and internal integrations.

Database Changes

  • traders
  • Added is_archived (Boolean, NOT NULL, default false)
    • Migration: a1b2c3d4e5f6_add_is_archived_to_traders.py (2026-02-02)
  • Added offices (Text, nullable)
  • Added floor (Integer, nullable)
  • Added stage (Integer, nullable)
  • Added style (Text, nullable)

    • Migration: e6f7a8b9c0d1_add_location_to_traders.py (2026-02-12)
  • groups

  • Added is_archived (Boolean, NOT NULL, default false)
    • Migration: d5e6f7a8b9c0_add_is_archived_to_groups.py (2026-02-11)

Removed Fields

  • trader_platforms.account_special_aliases was removed (dropped from schema)

7.1.2026

New Feature: Futures Price Data Endpoint

  • Added a new endpoint to retrieve OHLCV (Open, High, Low, Close, Volume) futures price data for contracts.
  • This endpoint complements the existing trade chart data endpoint to provide complete charting capabilities.

How to Use with Trade Chart Data

To display a complete trade chart on the frontend: 1. First, query GET api/v1/personal/trades/<trade_id>/chart-data/ to get the trade information including the contract name (e.g., "6A Mar26") 2. Then, query GET api/v1/futures-price-data/ with the contract, date range, and timeframe to get OHLCV candle data 3. Combine the data from both endpoints to render the complete chart

Important: Development Environment Configuration

In development environment, you must query the chart data and futures price data endpoints from production (api.skyll.space) instead of development (api.dev.skyll.space): - Development database only contains trades (copied nightly from production), not fills or futures price data - Trade IDs in development match production, so you can use them to query production endpoints - Query trades from development and tag the trades in development, but query chart-data and futures-price-data from production using the same trade ID

Timeframe Selection

  • Trades under 1 day: Use 1min candles
  • Trades over 1 day: Use 15min candles

API Changes

  • GET api/v1/futures-price-data/
    • New endpoint for retrieving futures market OHLCV data
    • Required query parameters:
      • contract (string): Contract identifier (e.g., "6A Mar26")
      • start_date (string, YYYY-MM-DD): Start date of the data range
      • end_date (string, YYYY-MM-DD): End date of the data range
    • Optional query parameters:
      • timeframe (string): Candle timeframe - "1min" (default), "5min", "15min", or "1hour"
    • Returns OHLCV candle data and metadata
    • Cached for ~2 hours (market data is public and shared across all users)

3.7.2025

❗BREAKING CHANGE❗ Comparative Performance by Time response structure simplified

  • The GET api/v1/comparative/performance-by-time/ endpoint response structure has been simplified.
  • Previously, each product contained detailed breakdowns of winning/losing traders and their P&L segments. Now, each product contains just the overall P&L for all traders combined.
  • Products within each day are now sorted from most profitable to least profitable.
  • The endpoint returns all products for each day, but frontend should display only the top 5 most profitable and bottom 5 least profitable products (similar to performance by product view).

API Changes

  • GET api/v1/comparative/performance-by-time/
    • Product object structure in the response has been simplified.
    • Products are now sorted by P&L in descending order within each day.

Example: Old Structure

{
  "date": "2023-01-02",
  "daynumber": 1,
  "products": [
    {
      "product": "Product1",
      "winning_top_5_pnl": 1000.00,
      "winning_top_5_traders": 1,
      "winning_rest_pnl": 500.00,
      "winning_rest_traders": 1,
      "losing_top_5_pnl": 0.00,
      "losing_top_5_traders": 0,
      "losing_rest_pnl": 0.00,
      "losing_rest_traders": 0,
      "total_traders": 2
    }
  ]
}

Example: New Structure

{
  "date": "2023-01-02",
  "daynumber": 1,
  "products": [
    {
      "product": "Product1",
      "pnl": 1500.00
    },
    {
      "product": "Product2", 
      "pnl": -200.00
    }
  ]
}

Frontend Implementation Guide

  • Products are automatically sorted from most profitable to least profitable within each day.
  • Display only the top 5 most profitable and bottom 5 least profitable products per day.
  • If there are fewer than 10 products total, display all available products.

16.6.2025

❗BREAKING CHANGE❗ Intraday PnL response structure updated for individual products

  • The data structure for individual products in the GET api/v1/personal/intraday-pnl/ endpoint has been updated to better support charting of separate trades.
  • Previously, each individual product entry contained a flat list of all its PnL candles for the day. Now, each product contains a trades array, where each object in the array represents a single trade and contains the trade_info and its associated candles .
  • This change allows the frontend to accurately render PnL line charts by clearly separating candles belonging to different trades.

API Changes

  • GET api/v1/personal/intraday-pnl/
    • The Individual product object structure in the response has been modified.

Example: Old Structure

"Individual": [
  {
    "product": { "id": 1, "symbol": "ES", ... },
    "candles": [
      { "datetime": "...", "open": 10.0, "close": 15.0, ... },
      { "datetime": "...", "open": 15.0, "close": 12.0, ... },
      // ... more candles from potentially different trades
    ]
  }
]

Example: New Structure

"Individual": [
  {
    "product": { "id": 1, "symbol": "ES", ... },
    "trades": [
      {
        "trade_info": { "id": "uuid-1", ... },
        "candles": [
          { "datetime": "...", "open": 10.0, "close": 15.0, ... },
          { "datetime": "...", "open": 15.0, "close": 20.0, ... }
        ]
      },
      {
        "trade_info": { "id": "uuid-2", ... },
        "candles": [
          { "datetime": "...", "open": 5.0, "close": 8.0, ... },
          { "datetime": "...", "open": 8.0, "close": 2.0, ... }
        ]
      }
    ]
  }
]

Frontend Implementation Guide

To correctly render the PnL line chart for an individual product, iterate through the trades array. For each trade's list of candles:

  1. Use the open value of the first candle as the starting point for that trade's line segment.
  2. Use the close value for all subsequent candles within that same trade. This will ensure that the PnL line remains flat between trades, accurately reflecting the periods of inactivity, instead of incorrectly drawing a line between the close of one trade and the open of the next.

16.5.2025

🎂 New Feature: Performance by time - Candlesticks

  • Added a new endpoint to retrieve detailed intraday daily Profit and Loss (PnL) data for a specific trader over a date range.
  • The endpoint provides PnL candlestick data (open, high, low, close) and total trades for:

    • Each individual product traded by the user.
    • An aggregated view across all products.
  • Supports querying by start_date and end_date .

  • Admin users can specify a trader ID to retrieve data for that trader.

API Changes

  • GET api/v1/personal/intraday-daily-pnl/
    • New endpoint.
    • Retrieves daily PnL candlestick data for individual products and aggregated PnL.
    • Query Parameters:
      • start_date (string, YYYY-MM-DD, required): Start date for the PnL data.
      • end_date (string, YYYY-MM-DD, required): End date for the PnL data.
      • trader (integer, optional): Trader ID to get data for (admin only). If not provided, data is shown for the authenticated user.

12.5.2025

🎨 New Feature: Product Color Added

  • The api/v1/products endpoint now includes a color field for each product. Frontend should always use this color for the given product across all charts.

API Changes

  • api/v1/products
    • Added color string parameter (hex code) to each product object in the response data array. Example response snippet:
{
  "data": [
    {
      "id": 11,
      "symbol": "BTC",
      "exchange": "CME",
      "name": "CME BITCOIN",
      "color": "#E3FFFD" << Added this
    },
    {
      "id": 14,
      "symbol": "CL",
      "exchange": "CME",
      "name": "CRUDE OIL FUTURES",
      "color": "#7A004C" <<Added this
    }
  ]
}

7.5.2025

🕯️ New Feature: Intraday PnL Endpoints

  • Added two new endpoints providing detailed 1-minute interval intraday Profit and Loss (PnL) candle data. These endpoints enable granular analysis of trading performance.

API Changes

  • GET /api/v1/personal/intraday-pnl/

    • Description: Retrieves 1-minute PnL candles for a specific trader and day.
    • Functionality: Shows individual product PnLs (with carry-forward logic for consecutive trades of the same product within the day) and an aggregated PnL across all products traded by the user.
    • Use Case: Allows a trader to analyze their intraday performance in detail, observe PnL evolution per product, and see overall PnL fluctuation minute by minute.
  • GET /api/v1/comparative/top-intraday/

    • Description: Fetches 1-minute PnL candles for the top N most profitable intraday trades within the authenticated user's trading group for a selected day.
    • Functionality: Displays PnL evolution for high-performing trades, allowing users to see how top trades developed. PnL data is obfuscated for non-admin users.
    • Use Case: Enables users to compare their performance against top intraday trades in their group and identify patterns in successful short-term trading.

25.3.2025

Security Improvement: Token Security Enhanced

  • Improved JWT token security throughout the system
  • Access token lifetime reduced from 24 hours to 1 hour
  • Refresh token lifetime reduced from 7 days to 3 days
  • Added token rotation and blacklisting for enhanced security

API Changes

  • POST api/v1/auth/token/revoke/

    • New endpoint to blacklist refresh tokens when logging out
    • Send your refresh token in the request body to invalidate it
    • Call this endpoint when users log out to prevent token reuse
  • Token refresh now returns a new refresh token with each successful request

    • Be sure to store the new refresh token after each refresh
  • Note: These changes are backward compatible with existing frontend implementations

24.3.2025

New Feature: Opt-out

  • Added a new opt-out feature for comparative analytics
  • When a user opts out:
    • Their trading data is excluded from comparative analytics shown to other users
    • They lose access to comparative analytics endpoints (returns 403 Forbidden)
    • They retain full access to their personal trading data and statistics
    • This setting can be toggled on/off at any time

API Changes

  • api/v1/auth/user

    • Added opt_out boolean parameter to indicate user's opt-out status
  • POST api/v1/auth/user/opt-out

    • New endpoint to toggle the user's opt-out setting

21.3.2025

❗BREAKING CHANGE❗ Products aggregation to "Others" category removed

  • Previously, some endpoints returned only the top 10 products (based on highest absolute PnL), with the remaining products aggregated into an "Others" category.
  • This approach was not technically ideal. Now, all products are returned—sorted by their absolute PnL—with the responsibility on the frontend to display only the top 10 (or any desired subset).
  • This does not change the structure of the endpoint response. The "Others" were previously returned as one of the "Products"
  • Affected endpoints:
    • api/v1/comparative/calendar-events
    • api/v1/personal/calendar-events
    • api/v1/comparative/performance-by-time

3.3.2025

Welcome Prompt

  • Endpoint added:

    • api/v1/welcome-prompt
  • Should be used to load the initial welcome message

  • No need to use any query parameters for now
  • Just call the GET endpoint and read its prompt_message value which is then displayed in the welcome prompt
  • Only display this prompt once for every user. Keep track of this on the FE part

Endpoint response

{
  "data": [
    {
      "id": 5,
      "date": "2025-03-03",
      "prompt_message": "Last week's standout asset was ES. What trading strategies could you develop to better capitalize on similar market conditions?",
      "group_id": 1,
      "trader_id": null,
      "prompt_type": "weekly",
      "product_symbol": "ES"
    }
  ],
  "meta": {
    "date": "2025-03-03",
    "group_id": 1,
    "trader_id": 24
  }
}

17.2.2025

Calendar Events - Trader products

  • Endpoints added:
  • api/v1/comparative/calendar-events/traded-products
  • api/v1/personal/calendar-events/traded-products These endpoints are used to get a list of products that were traded during specific days shown in the calendar events. They will fill the filters in the Calendar Events charts with only the products that were active during the selected time period. Every time a user changes the start date, end date, or any calendar event filter options, you must call these endpoints again to update the list of products.

This is similar to fetching products from personal/traded-products for the personal analytics.

13.2.2025

Calendar Events

  • Endpoints added:

    • api/v1/calendar-events
    • api/v1/comparative/calendar-events
    • api/v1/personal/calendar-events
    • api/v1/products
  • Comments added to all endpoints. Check them for these endpoint descriptions.

❗BREAKING CHANGE❗ - Product ID query parameter

The product query parameter now accepts an integer product id instead of a string product symbol for these two endpoints:

  • api/v1/personal/performance-by-pnl
  • api/v1/personal/win-loss-ratio You can find the ids in the endpoint below:

❗BREAKING CHANGE❗ - Trader products endpoint response structure

The api/v1/personal/traded-products endpoint response structure has been extended and changed.

Previously:

"data": [
{
  "product": "6C",
},
{
  "product": "6E",
},

Now:

"data": [
{
  "symbol": "6C",
  "id": 3
},
{
  "symbol": "6E",
  "id": 4
},