SkyTrackPro API v1 - Developer Guide

This document describes the current /api/v1 gateway for login, IGC upload, live tracking, flight retrieval, and task access.

v1
Base URL for all endpoints:
https://skytrackpro.app/api/v1/...
Example endpoints: POST /api/v1/auth/login, POST /api/v1/flights/upload, GET /api/v1/flights, POST /api/v1/live/start, GET /api/v1/tasks

1. Introduction

The SkyTrackPro API allows trusted clients such as mobile apps, companion tools, and integrations to:

  • Log users in, refresh tokens, and log out
  • Upload and analyze IGC flight files
  • List and retrieve uploaded flights
  • Start and stop live tracking sessions and push live positions
  • Query active live sessions, tracks, tails, and live users
  • Fetch task definitions for import and navigation

Every request requires a valid API key. User-specific actions additionally require a Bearer access token.

2. Response envelope

Every JSON response uses the same envelope. Payload data lives under data; errors carry a stable error_code string in addition to the human-readable message.

Success
{
  "ok": true,
  "message": "Login successful.",
  "data": {
    "token": "...",
    "refresh_token": "...",
    "user": { "id": 123, "email": "pilot@example.com" }
  }
}
Error
{
  "ok": false,
  "message": "Invalid credentials",
  "error_code": "INVALID_CREDENTIALS",
  "data": null
}

3. API Keys

The /api/v1 gateway accepts active keys from jscms_skytrack_api_clients. For personal integrations, a user can generate a personal API key from the SkyTrackPro profile / flight profile area and send it with every request.

Header usage
GET /api/v1/auth/me HTTP/1.1
Host: skytrackpro.app
X-Api-Key: <YOUR_API_KEY>
Authorization: Bearer <ACCESS_TOKEN>
Query parameter fallback
GET /api/v1/auth/me?api_key=<YOUR_API_KEY> HTTP/1.1
Host: skytrackpro.app
Authorization: Bearer <ACCESS_TOKEN>
Errors:
Missing key: error_code: MISSING_API_KEY (HTTP 401)
Invalid or inactive key: error_code: INVALID_API_KEY (HTTP 401)

4. User Authentication

User authentication uses the existing JScms login system and issues opaque access tokens (short-lived) and refresh tokens (long-lived).

  • Access token lifetime: about 1 day
  • Refresh token lifetime: about 90 days
  • Store both tokens securely and refresh the access token when needed
  • Call POST /auth/logout on sign-out to revoke them

4.1 Login - POST /api/v1/auth/login

Request body:

{
  "email": "pilot@example.com",
  "password": "secret-password",
  "remember": true
}

Example:

curl -X POST \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: <YOUR_API_KEY>" \
  -d '{"email":"pilot@example.com","password":"secret-password","remember":true}' \
  "https://skytrackpro.app/api/v1/auth/login"

Success response:

{
  "ok": true,
  "message": "Login successful.",
  "data": {
    "token": "64-char-hex-access-token",
    "refresh_token": "64-char-hex-refresh-token",
    "user": {
      "id": 123,
      "email": "pilot@example.com"
    }
  }
}

4.2 Current user - GET /api/v1/auth/me

Requires a Bearer token (or an existing logged-in browser session).

{
  "ok": true,
  "message": "",
  "data": {
    "user": {
      "id": 123,
      "email": "pilot@example.com",
      "name": "Pilot Name",
      "usergroupid": 5,
      "access": 1,
      "public": 1
    }
  }
}

4.3 Refresh token - POST /api/v1/auth/refresh

Request body:

{
  "refresh_token": "64-char-hex-refresh-token"
}

Success response:

{
  "ok": true,
  "message": "Token refreshed.",
  "data": {
    "token": "new-64-char-hex-access-token",
    "refresh_token": "64-char-hex-refresh-token",
    "user": {
      "id": 123,
      "email": "pilot@example.com",
      "name": "Pilot Name",
      "usergroupid": 5,
      "access": 1,
      "public": 1
    }
  }
}

4.4 Logout - POST /api/v1/auth/logout

Revokes the access token in the Authorization header. If a refresh_token belonging to the same user is supplied, it is revoked as well. Idempotent: revoking an already-revoked token returns error_code: TOKEN_REVOKED on the next attempt.

// Request
{
  "refresh_token": "optional-64-char-hex-refresh-token"
}

// Response
{
  "ok": true,
  "message": "Logged out.",
  "data": null
}

5. Flights

5.1 Upload IGC - POST /api/v1/flights/upload

Requirements:
  • Valid API key
  • Valid Bearer access token
  • User must belong to a SkyTrack-enabled member group with track access
  • User must confirm file rights / airspace responsibility with agree or st_agree set to 1
Upload policy:
  • Normal SkyTrack members can upload flights through the API with the same track-access entitlement used on the website
  • Free-tier users can upload up to 12 flights from the current calendar year
  • Flights from previous calendar years are rejected
  • Older current-year flights may be accepted as private but locked pending approval when they exceed the configured upload-age window

Multipart form-data

  • file - required .igc file
  • title - optional
  • visibility - optional, one of private, org, public, unlisted
  • org_id - optional
  • task_id - optional
  • st_agree or agree - required, must be 1

JSON upload

{
  "file_base64": "<base64 encoded IGC>",
  "file_name": "flight.igc",
  "title": "Optional title",
  "visibility": "private",
  "org_id": 5,
  "task_id": 123,
  "agree": 1
}

Compatibility fallback: if visibility is omitted, the gateway also accepts is_public and maps it to public or private.

Success response:

{
  "ok": true,
  "message": "IGC uploaded and analyzed.",
  "data": {
    "track_id": 987,
    "duplicate": false,
    "date": "2025-06-13",
    "year": 2025,
    "visibility": "private",
    "late_upload": false,
    "notices": [],
    "meta": { "...": "full analysis data" }
  }
}

HTTP status is 201 for a new upload and 200 for a duplicate IGC that already exists in the same user's tracks.

5.2 List flights - GET /api/v1/flights

Requires a Bearer token. Returns flights visible to the requester.

Optional query parameters:

  • user_id - filter to a specific user. When this is not the requester, only that user's public flights are returned. Default: requester's own flights.
  • visibility - private | org | public | unlisted. Honored when listing your own flights; ignored (forced to public) when listing another user.
  • from, to - YYYY-MM-DD bounds on date_flown
  • limit - default 50, max 200
  • offset - default 0
  • order - date_desc (default) or date_asc
{
  "ok": true,
  "message": "",
  "data": {
    "flights": [
      {
        "id": 987,
        "user_id": 123,
        "title": "Saturday Aletsch",
        "date_flown": "2025-06-13",
        "duration_s": 9450,
        "distance_m": 87420,
        "longest_xc_m": 73210,
        "longest_xc_key": "free3",
        "xc_points": 91.45,
        "max_alt_m": 3640,
        "gain_total_m": 4120,
        "avg_speed_kmh": 33.4,
        "max_speed_kmh": 56.1,
        "launch":  { "lat": 46.41, "lon": 8.13 },
        "landing": { "lat": 46.27, "lon": 8.04 },
        "visibility": "private",
        "takeoff_id": 412,
        "analysis_status": "done",
        "is_competition": false,
        "comp_task_id": null,
        "created_at": "2025-06-13 18:22:01"
      }
    ],
    "count":  1,
    "limit":  50,
    "offset": 0
  }
}

5.3 Get flight - GET /api/v1/flights/{id}

Requires a Bearer token.

  • Owner: any visibility, including locked / pending-review flights (check analysis_status)
  • Non-owner: public or unlisted only, and only when active (else 403 FORBIDDEN or 404 NOT_FOUND)

The detail response adds notes, bbox, share_token, analysis_error, fai_class, wing_hg_type, tz_offset_min, and updated_at to the list shape above.

6. Live Tracking

The live API stores sessions and positions in the dedicated live-tracking database. All endpoints (read and write) require a Bearer token. Read endpoints return only sessions that are public (privacy_level = 0) or owned by the requester.

6.1 Start session - POST /api/v1/live/start

{
  "task_id": 123,
  "group_id": 5,
  "mode": "freeflight",
  "device_id": "device-uuid",
  "device_label": "Phone",
  "privacy_level": 0,
  "client_version": "1.2.3",
  "app_version": "1.2.3"
}
  • privacy_level: 0 = public (visible to anyone authenticated). Any other value = private (only the owner can read it).
  • group_id identifies the session group (e.g. competition, organization)
  • device_id is preferred; if omitted, device_label is used as the stored identifier
  • client_version is preferred; app_version is accepted as an alias
  • mode is accepted for compatibility but is not currently stored or used as a server-side filter
  • Starting again on the same user_id + device_id can return the same session_id until the cleanup cron removes that row
{
  "ok": true,
  "message": "Live session created.",
  "data": { "session_id": 456 }
}

Success status: 201 for a new session, 200 when an existing session is reused or reactivated.

6.2 Stop session - POST /api/v1/live/stop

{
  "session_id": 456,
  "end_reason": 1
}

Stopping ends the session for now, but the next start on the same user/device may reactivate the same session_id until cleanup archives it.

6.3 Send points - POST /api/v1/live/tick

{
  "session_id": 456,
  "points": [
    {
      "ts": 1730784300,
      "lat": 46.123456,
      "lon": 7.123456,
      "alt": 1500,
      "speed": 11.2,
      "heading": 230,
      "accuracy": 8.5,
      "h_acc": 8.5,
      "source": 2,
      "seq": 1
    }
  ]
}
  • ts is a Unix timestamp in seconds
  • accuracy is preferred; h_acc is accepted as a compatibility alias
  • source and seq are optional and preserved in stored positions

6.4 Active sessions - GET /api/v1/live/sessions

Requires a Bearer token. Returns only sessions that are public (privacy_level = 0) or owned by the requester.

Optional query parameters: group_id, task_id

6.5 Session track - GET /api/v1/live/session/{id}/track

Requires a Bearer token. Returns 403 FORBIDDEN when the session is private and the requester is not the owner.

Optional query parameters: limit (default 500, max 5000), since_ts

6.6 Session tail - GET /api/v1/live/session/{id}/tail

Returns the latest stored point for a session. Same auth and privacy rules as /track.

6.7 Live users overview - GET /api/v1/live/users

Requires a Bearer token. Returns markers for sessions that are public or owned by the requester.

Optional query parameters:

  • group_id
  • task_id
  • mode - accepted for compatibility; currently echoed back but not used as a real server-side filter
  • since_ts
  • public_only - 0 or 1 (filters by user.public)
  • limit - default 200, max 1000

7. Tasks

7.1 List tasks - GET /api/v1/tasks

Optional query parameters: org_id, category_id, published

This endpoint works with only the API key. Supplying a Bearer token is optional but may affect competition-linked visibility checks.

7.2 Get task details - GET /api/v1/tasks/{id}

Returns full task metadata including defaults, task_board, spec, and calc.

Competition-linked tasks may return 403 FORBIDDEN when the current viewer does not pass the task access gate.

8. Error codes

Failures always include a stable error_code string. Branch on the code, not on the human-readable message.

HTTPerror_codeMeaning
400VALIDATION_ERRORA required field is missing or malformed
400AGREE_REQUIREDIGC upload was not confirmed with agree=1
400SESSION_INACTIVELive session is missing, ended, or owned by someone else
400UNSUPPORTED_VERSIONPath uses a version other than v1
400 / 500UPLOAD_FAILEDIGC analyzer rejected the file or threw
401MISSING_API_KEYX-Api-Key header / api_key query missing
401INVALID_API_KEYKey not recognized or marked inactive
401UNAUTHENTICATEDEndpoint requires a Bearer token
401INVALID_TOKENBearer token not found
401TOKEN_REVOKEDBearer or refresh token was revoked (typically by logout)
401TOKEN_EXPIREDToken is past its expiry
401TOKEN_NOT_FOR_CLIENTRefresh token was issued to a different API client
401INVALID_CREDENTIALSEmail / password did not match
401LOGIN_FAILEDInternal error during login (rare)
403FORBIDDENAuthenticated, but not allowed (private session, gated task, no track access)
404NOT_FOUNDResource (user, session, flight, task) does not exist or is inactive
404UNKNOWN_RESOURCEUnknown top-level resource in the URL
404UNKNOWN_ENDPOINTResource exists but the action / method does not
500SERVER_ERRORUnexpected server-side failure

9. Quick Start

  1. Generate or retrieve a valid API key from the SkyTrackPro profile / flight profile area.
  2. Call POST /api/v1/auth/login once and store both data.token and data.refresh_token.
  3. Send X-Api-Key with every request.
  4. Send Authorization: Bearer ... for write endpoints, live read endpoints, and other user-specific calls.
  5. When the access token expires (error_code: TOKEN_EXPIRED), call POST /api/v1/auth/refresh and retry the original request.
  6. On user sign-out, call POST /api/v1/auth/logout to revoke both tokens, then drop them locally.
  7. If you rotate the API key in the profile, update your integration immediately.