API Documentation

April 4, 2026 ยท View on GitHub

Base URL: http://localhost:8009/api/v1 (dev) | https://tv.cadnative.com/api/v1 (prod)

Conventions

Auth: All authenticated endpoints accept either header:

  • X-Session-Id: <session_id> (session-based)
  • Authorization: Bearer <access_token> (JWT)

Response format: All responses return { "success": true/false, ... }. Errors include an error field.

Rate limiting: See Rate Limiting table at the bottom.

sequenceDiagram
    participant Client
    participant API
    participant MongoDB

    alt Session Auth
        Client->>API: POST /auth/login {username, password}
        API->>MongoDB: Verify credentials (bcrypt)
        API-->>Client: {sessionId, user}
        Client->>API: GET /channels (X-Session-Id)
    else JWT Auth
        Client->>API: POST /jwt/login {username, password}
        API-->>Client: {accessToken, refreshToken, user}
        Client->>API: GET /channels (Authorization: Bearer)
    end

Authentication Endpoints

1. Login

POST /auth/login

Authenticate user and create a session.

Request Body:

{
  "username": "superadmin",
  "password": "ChangeMeNow123!"
}

Response (200 OK):

{
  "success": true,
  "sessionId": "abc123def456...",
  "user": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "superadmin",
    "email": "admin@firevision.local",
    "role": "Admin",
    "channelListCode": "ABC123",
    "isActive": true,
    "lastLogin": "2024-11-19T10:30:00.000Z"
  }
}

Error Response (401 Unauthorized):

{
  "success": false,
  "error": "Invalid credentials"
}

2. Logout

POST /auth/logout

Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "message": "Logged out successfully"
}

3. Get Current User

GET /auth/me

Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "user": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "superadmin",
    "email": "admin@firevision.local",
    "role": "Admin",
    "channelListCode": "ABC123",
    "isActive": true,
    "lastLogin": "2024-11-19T10:30:00.000Z",
    "channels": [],
    "metadata": {},
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-11-19T10:30:00.000Z"
  }
}

4. Change Password

POST /auth/change-password

Headers: X-Session-Id: <session_id>

Request Body:

{
  "currentPassword": "OldPassword123!",
  "newPassword": "NewPassword456!"
}

Response (200 OK):

{
  "success": true,
  "message": "Password changed successfully. Other sessions have been logged out."
}

5. Get All Sessions

GET /auth/sessions

Get all active sessions for the current user.

Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "sessions": [
    {
      "id": "64f7a8b9c1d2e3f4a5b6c7d8",
      "sessionId": "abc123def456...",
      "ipAddress": "192.168.1.100",
      "userAgent": "Mozilla/5.0...",
      "createdAt": "2024-11-19T10:00:00.000Z",
      "expiresAt": "2024-11-20T10:00:00.000Z",
      "lastActivity": "2024-11-19T10:30:00.000Z",
      "isCurrent": true
    }
  ]
}

6. Revoke Session

DELETE /auth/sessions/:sessionId

Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "message": "Session revoked successfully"
}

JWT Authentication

1. JWT Login

POST /jwt/login

Authenticate user and receive JWT access and refresh tokens.

Request Body:

{
  "username": "user",
  "password": "pass"
}

Response (200 OK):

{
  "success": true,
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "user",
    "role": "User",
    "channelListCode": "ABC123"
  }
}

2. Refresh Access Token

POST /jwt/refresh

Exchange a refresh token for a new access token.

Request Body:

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

Response (200 OK):

{
  "success": true,
  "accessToken": "eyJhbGciOiJIUzI1NiIs..."
}

3. JWT Logout

POST /jwt/logout

Invalidate a refresh token.

Request Body:

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

Response (200 OK):

{
  "success": true,
  "message": "Logged out successfully"
}

4. Get Current User (JWT)

GET /jwt/me

Headers: Authorization: Bearer <access_token>

Response (200 OK):

{
  "success": true,
  "user": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "user",
    "email": "user@example.com",
    "role": "User",
    "channelListCode": "ABC123",
    "isActive": true
  }
}

5. Get Playlist (JWT)

GET /jwt/playlist.m3u

Returns the authenticated user's M3U playlist.

Headers: Authorization: Bearer <access_token>

Response (200 OK):

#EXTM3U
#EXTINF:-1 tvg-id="hbo-hd" tvg-name="HBO HD" tvg-logo="http://example.com/logos/hbo.png" group-title="Movies",HBO HD
http://example.com/stream/hbo.m3u8

Public Signup

1. Register New Account

POST /public/signup

Create a new user account. Rate limited to 10 requests per hour.

Auth: Not required

Request Body:

{
  "username": "new_user",
  "email": "newuser@example.com",
  "password": "SecurePass123!"
}

Response (201 Created):

{
  "success": true,
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "new_user",
    "email": "newuser@example.com",
    "role": "User",
    "channelListCode": "XYZ789"
  }
}

User Management Endpoints

1. Get All Users

GET /users

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "count": 5,
  "data": [
    {
      "id": "64f7a8b9c1d2e3f4a5b6c7d8",
      "username": "john_doe",
      "email": "john@example.com",
      "role": "User",
      "channelListCode": "XYZ789",
      "isActive": true,
      "lastLogin": "2024-11-19T09:00:00.000Z",
      "channels": [
        {
          "channelName": "HBO",
          "channelGroup": "Movies"
        }
      ],
      "createdAt": "2024-01-15T00:00:00.000Z",
      "updatedAt": "2024-11-19T09:00:00.000Z"
    }
  ]
}

2. Create User

POST /users

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Request Body:

{
  "username": "jane_doe",
  "password": "SecurePass123!",
  "email": "jane@example.com",
  "role": "User",
  "isActive": true
}

Response (201 Created):

{
  "success": true,
  "message": "User created successfully",
  "data": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "jane_doe",
    "email": "jane@example.com",
    "role": "User",
    "channelListCode": "DEF456",
    "isActive": true
  }
}

3. Get User by ID

GET /users/:id

Auth Required: Admin or own profile Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "data": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "jane_doe",
    "email": "jane@example.com",
    "role": "User",
    "channelListCode": "DEF456",
    "isActive": true,
    "channels": [],
    "lastLogin": null,
    "createdAt": "2024-11-19T10:00:00.000Z"
  }
}

4. Update User

PUT /users/:id

Auth Required: Admin or own profile Headers: X-Session-Id: <session_id>

Request Body:

{
  "username": "jane_smith",
  "email": "jane.smith@example.com",
  "password": "NewPassword123!",
  "role": "Admin",
  "isActive": false
}

Note: Only admins can change role and isActive fields.

Response (200 OK):

{
  "success": true,
  "message": "User updated successfully",
  "data": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "username": "jane_smith",
    "email": "jane.smith@example.com",
    "role": "Admin",
    "isActive": false
  }
}

5. Delete User

DELETE /users/:id

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "message": "User deleted successfully"
}

6. Assign Channels to User

POST /users/:id/channels

Replace all user channels with the provided list.

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Request Body:

{
  "channelIds": ["64f7a8b9c1d2e3f4a5b6c7d8", "64f7a8b9c1d2e3f4a5b6c7d9"]
}

Response (200 OK):

{
  "success": true,
  "message": "Channels assigned successfully",
  "data": {
    "userId": "64f7a8b9c1d2e3f4a5b6c7d8",
    "channelsCount": 2
  }
}

7. Add Channels to User

POST /users/:id/channels/add

Add channels to user without removing existing ones.

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Request Body:

{
  "channelIds": ["64f7a8b9c1d2e3f4a5b6c7da"]
}

Response (200 OK):

{
  "success": true,
  "message": "Added 1 channels",
  "data": {
    "userId": "64f7a8b9c1d2e3f4a5b6c7d8",
    "channelsCount": 3,
    "addedCount": 1
  }
}

8. Remove Channels from User

POST /users/:id/channels/remove

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Request Body:

{
  "channelIds": ["64f7a8b9c1d2e3f4a5b6c7d8"]
}

Response (200 OK):

{
  "success": true,
  "message": "Removed 1 channels",
  "data": {
    "userId": "64f7a8b9c1d2e3f4a5b6c7d8",
    "channelsCount": 2,
    "removedCount": 1
  }
}

9. Regenerate Playlist Code

PUT /users/:id/regenerate-code

Auth Required: Admin or own profile Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "message": "Playlist code regenerated successfully",
  "data": {
    "channelListCode": "GHI789"
  }
}

User Playlist

Endpoints for users to manage their own channel playlists.

1. Get My Channels

GET /user-playlist/me/channels

Auth Required: Yes (Session or JWT)

Response (200 OK):

{
  "success": true,
  "count": 5,
  "data": [
    {
      "id": "64f7a8b9c1d2e3f4a5b6c7d8",
      "channelName": "HBO HD",
      "channelUrl": "http://example.com/stream/hbo.m3u8",
      "channelImg": "http://example.com/logos/hbo.png",
      "channelGroup": "Movies"
    }
  ]
}

2. Replace My Channels

PUT /user-playlist/me/channels

Replace all channels in the user's playlist.

Auth Required: Yes (Session or JWT)

Request Body:

{
  "channelIds": ["64f7a8b9c1d2e3f4a5b6c7d8", "64f7a8b9c1d2e3f4a5b6c7d9"]
}

Response (200 OK):

{
  "success": true,
  "message": "Channels updated successfully",
  "data": {
    "channelsCount": 2
  }
}

3. Add Channels to My Playlist

POST /user-playlist/me/channels/add

Add channels without removing existing ones.

Auth Required: Yes (Session or JWT)

Request Body:

{
  "channelIds": ["64f7a8b9c1d2e3f4a5b6c7da"]
}

Response (200 OK):

{
  "success": true,
  "message": "Added 1 channels",
  "data": {
    "channelsCount": 6,
    "addedCount": 1
  }
}

4. Remove Channels from My Playlist

POST /user-playlist/me/channels/remove

Auth Required: Yes (Session or JWT)

Request Body:

{
  "channelIds": ["64f7a8b9c1d2e3f4a5b6c7d8"]
}

Response (200 OK):

{
  "success": true,
  "message": "Removed 1 channels",
  "data": {
    "channelsCount": 5,
    "removedCount": 1
  }
}

5. Get My M3U Playlist

GET /user-playlist/me/playlist.m3u

Returns the authenticated user's channels as an M3U playlist file.

Auth Required: Yes (Session or JWT)

Response (200 OK):

#EXTM3U
#EXTINF:-1 tvg-id="hbo-hd" tvg-name="HBO HD" tvg-logo="http://example.com/logos/hbo.png" group-title="Movies",HBO HD
http://example.com/stream/hbo.m3u8

Channel Management Endpoints

1. Get All Channels

GET /channels

Auth: Not required

Response (200 OK):

{
  "success": true,
  "count": 150,
  "data": [
    {
      "id": "64f7a8b9c1d2e3f4a5b6c7d8",
      "channelId": "hbo-hd",
      "channelName": "HBO HD",
      "channelUrl": "http://example.com/stream/hbo.m3u8",
      "channelImg": "http://example.com/logos/hbo.png",
      "channelGroup": "Movies",
      "isActive": true,
      "order": 1,
      "alternateStreams": [
        { "streamUrl": "http://example.com/alt1.m3u8", "quality": "720p" },
        { "streamUrl": "http://example.com/alt2.m3u8", "quality": null }
      ]
    }
  ]
}

Note: alternateStreams includes up to 3 alive, non-flagged alternate stream URLs with quality info. Dead or flagged alternates are excluded.


2. Get Channels Grouped by Category

GET /channels/grouped

Auth: Not required

Response (200 OK):

{
  "success": true,
  "data": {
    "Movies": [
      {
        "id": "64f7a8b9c1d2e3f4a5b6c7d8",
        "channelName": "HBO HD",
        "channelUrl": "http://example.com/stream/hbo.m3u8"
      }
    ],
    "Sports": []
  }
}

3. Search Channels

GET /channels/search?q=hbo

Auth: Not required

Query Parameters:

  • q (required): Search query

Response (200 OK):

{
  "success": true,
  "count": 3,
  "data": [
    {
      "id": "64f7a8b9c1d2e3f4a5b6c7d8",
      "channelName": "HBO HD",
      "channelGroup": "Movies"
    }
  ]
}

Stream Metrics Endpoints

1. Report Stream Status

POST /channels/:id/report-status

Auth: TV code or session auth

Request Body:

{
  "status": "dead",
  "deviceId": "device-abc-123"
}
  • status (required): One of dead, alive, unresponsive
  • deviceId (required): Unique device identifier

Rate Limit: 1 report per channel per device per 5 minutes

Response (200 OK):

{
  "success": true,
  "data": {
    "channelId": "64f7a8b9c1d2e3f4a5b6c7d8",
    "status": "dead",
    "metrics": {
      "deadCount": 5,
      "aliveCount": 42,
      "unresponsiveCount": 1,
      "playCount": 30,
      "lastDeadAt": "2026-03-18T10:00:00.000Z"
    }
  }
}

2. Report Successful Playback

POST /channels/:id/report-play

Auth: TV code or session auth

Request Body:

{
  "deviceId": "device-abc-123",
  "proxyPlay": false,
  "streamUrl": "http://example.com/alt-stream.m3u8"
}
  • deviceId (required): Unique device identifier
  • proxyPlay (optional): Whether playback used the server proxy
  • streamUrl (optional): The actual stream URL that played. If this differs from the channel's primary channelUrl and matches an alternate stream, the server auto-promotes it: the alternate becomes the new primary and the old primary is demoted to alternates with liveness.status = dead

Rate Limit: 1 report per channel per device per 1 minute

Response (200 OK):

{
  "success": true,
  "data": {
    "channelId": "64f7a8b9c1d2e3f4a5b6c7d8",
    "metrics": {
      "deadCount": 5,
      "aliveCount": 42,
      "unresponsiveCount": 1,
      "playCount": 31,
      "lastPlayedAt": "2026-03-18T10:05:00.000Z"
    }
  }
}

3. Bulk Health Sync

POST /channels/health-sync

Auth: TV code or session auth

Request Body:

{
  "deviceId": "device-abc-123",
  "reports": [
    { "channelId": "64f7a8b9c1d2e3f4a5b6c7d8", "status": "alive" },
    { "channelId": "64f7a8b9c1d2e3f4a5b6c7d9", "status": "dead" },
    { "channelId": "64f7a8b9c1d2e3f4a5b6c7da", "status": "played" }
  ]
}
  • reports array: max 100 items, each with channelId and status (dead, alive, unresponsive, played)

Rate Limit: 1 sync per device per 5 minutes

Response (200 OK):

{
  "success": true,
  "data": {
    "updated": 2,
    "failed": 0,
    "skipped": 1
  }
}

Admin Channel Endpoints

1. Get All Channels (Including Inactive)

GET /admin/channels

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "count": 200,
  "data": []
}

2. Create Channel

POST /admin/channels

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Request Body:

{
  "channelId": "espn-hd",
  "channelName": "ESPN HD",
  "channelUrl": "http://example.com/stream/espn.m3u8",
  "channelImg": "http://example.com/logos/espn.png",
  "channelGroup": "Sports",
  "isActive": true,
  "order": 10
}

Response (201 Created):

{
  "success": true,
  "data": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "channelId": "espn-hd",
    "channelName": "ESPN HD"
  }
}

3. Update Channel

PUT /admin/channels/:id

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Request Body:

{
  "channelName": "ESPN HD Updated",
  "isActive": false
}

Response (200 OK):

{
  "success": true,
  "data": {
    "id": "64f7a8b9c1d2e3f4a5b6c7d8",
    "channelName": "ESPN HD Updated",
    "isActive": false
  }
}

4. Delete Channel

DELETE /admin/channels/:id

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Response (200 OK):

{
  "success": true,
  "message": "Channel deleted successfully"
}

5. Import M3U Playlist

POST /admin/channels/import-m3u

Auth Required: Admin only Headers: X-Session-Id: <session_id>

Request Body:

{
  "m3uUrl": "http://example.com/playlist.m3u",
  "replaceExisting": false
}

Response (200 OK):

{
  "success": true,
  "message": "Imported 50 channels successfully",
  "imported": 50,
  "skipped": 5,
  "errors": 0
}

TV/Playlist Endpoints

1. Get Playlist by Code

GET /tv/playlist/:code

Get M3U playlist for a user by their 6-character playlist code.

Auth: Not required

Response (200 OK):

#EXTM3U
#EXTINF:-1 tvg-id="hbo-hd" tvg-name="HBO HD" tvg-logo="http://example.com/logos/hbo.png" group-title="Movies",HBO HD
http://example.com/stream/hbo.m3u8

2. Get Playlist as JSON

GET /tv/playlist/:code/json

Auth: Not required

Response (200 OK):

{
  "success": true,
  "user": {
    "username": "john_doe",
    "channelListCode": "XYZ789"
  },
  "channels": [
    {
      "channelName": "HBO HD",
      "channelUrl": "http://example.com/stream/hbo.m3u8",
      "channelGroup": "Movies"
    }
  ]
}

3. Verify Playlist Code

GET /tv/verify/:code

Auth: Not required

Response (200 OK):

{
  "success": true,
  "valid": true,
  "user": {
    "username": "john_doe",
    "isActive": true
  }
}

4. Pair Device

POST /tv/pair

Auth: Not required

Request Body:

{
  "code": "XYZ789",
  "deviceName": "Samsung Smart TV",
  "deviceModel": "QN65Q80TAFXZA"
}

Response (200 OK):

{
  "success": true,
  "message": "Device paired successfully",
  "user": {
    "username": "john_doe",
    "channelListCode": "XYZ789"
  }
}

PIN-Based TV Pairing

A secure pairing flow where the TV displays a PIN that the user enters on the web dashboard.

1. Request Pairing PIN

POST /tv/pairing/request

TV generates a PIN to display on screen. The user then enters this PIN on the web dashboard.

Auth: Not required

Request Body:

{
  "deviceName": "Samsung TV",
  "deviceModel": "QN65"
}

Response (200 OK):

{
  "success": true,
  "pin": "842736",
  "expiresAt": "2026-03-16T10:10:00.000Z",
  "expiryMinutes": 10,
  "message": "Enter this PIN on the web dashboard to pair your device"
}

2. Check Pairing Status

GET /tv/pairing/status/:pin

TV polls this endpoint to check whether the user has confirmed pairing.

Auth: Not required

Response (200 OK) - Pending:

{
  "success": true,
  "status": "pending",
  "message": "Waiting for user to confirm pairing"
}

Response (200 OK) - Completed:

{
  "success": true,
  "status": "completed",
  "user": {
    "username": "john_doe",
    "channelListCode": "5T6FEP",
    "role": "User"
  }
}

Response (410 Gone) - Expired:

{
  "success": false,
  "status": "expired",
  "message": "Pairing PIN has expired"
}

3. Confirm Pairing

POST /tv/pairing/confirm

Web dashboard confirms the pairing by submitting the PIN.

Auth Required: Yes Headers: X-Session-Id: <session_id>

Request Body:

{
  "pin": "842736",
  "sessionId": "abc123..."
}

Response (200 OK):

{
  "success": true,
  "message": "Device paired successfully",
  "device": {
    "name": "Samsung TV",
    "model": "QN65"
  },
  "user": {
    "username": "john_doe",
    "channelListCode": "5T6FEP",
    "role": "User"
  }
}

App Version Management

1. Check for Updates

GET /app/version?currentVersion=100

Auth: Not required

Query Parameters:

  • currentVersion (required): Current app version code

Response (200 OK):

{
  "success": true,
  "updateAvailable": true,
  "latestVersion": "v1.5",
  "latestVersionCode": 5,
  "currentVersion": 100,
  "isMandatory": false,
  "releaseNotes": "Bug fixes and improvements",
  "downloadUrl": "https://github.com/akshaynikhare/FireVisionIPTV/releases/download/v1.5/app-release.apk",
  "minCompatibleVersion": 1
}

2. Get Latest Version

GET /app/latest

Auth: Not required

Response (200 OK):

{
  "success": true,
  "data": {
    "versionName": "v1.5",
    "versionCode": 5,
    "releaseNotes": "Bug fixes and improvements",
    "apkFileName": "app-release.apk",
    "apkFileSize": 25600000,
    "downloadUrl": "https://github.com/.../app-release.apk",
    "isMandatory": false,
    "releasedAt": "2026-01-15T10:00:00.000Z"
  }
}

3. Download Latest APK

GET /app/apk

Auth: Not required

Redirects to the latest APK download URL on GitHub Releases.


4. Get Download URL

GET /app/download-url

Returns the download URL as JSON instead of redirecting.

Auth: Not required

Response (200 OK):

{
  "success": true,
  "downloadUrl": "https://github.com/akshaynikhare/FireVisionIPTV/releases/download/v1.5/app-release.apk"
}

5. Get Version History

GET /app/versions

Returns version history. Versions are managed via GitHub Releases.

Auth: Not required

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "versionName": "v1.5",
      "versionCode": 5,
      "releaseNotes": "Bug fixes and improvements",
      "downloadUrl": "https://github.com/.../app-release.apk",
      "releasedAt": "2026-01-15T10:00:00.000Z"
    }
  ]
}

Config Endpoints

1. Get Default Configuration

GET /config/defaults

Returns default configuration values for client applications.

Auth: Not required

Response (200 OK):

{
  "success": true,
  "data": {
    "defaultTvCode": "5T6FEP",
    "defaultServerUrl": "https://tv.cadnative.com",
    "pairingPinExpiryMinutes": 10,
    "appName": "FireVision IPTV",
    "version": "1.0.0"
  }
}

2. Get Server Info

GET /config/info

Returns server name, version, and available features.

Auth: Not required

Response (200 OK):

{
  "success": true,
  "data": {
    "name": "FireVision IPTV Server",
    "version": "1.0.0",
    "features": {
      "publicSignup": true,
      "pinPairing": true,
      "jwtAuth": true
    }
  }
}

Proxy Endpoints

1. Image Proxy

GET /image-proxy?url=<encoded_url>

Proxies and caches channel logo images. Cached with a 24-hour TTL.

Auth: Not required

Query Parameters:

  • url (required): URL-encoded image URL

Response: Proxied image with appropriate content-type headers.


2. Stream Proxy

GET /stream-proxy?url=<encoded_url>

Proxies HLS and other media streams with CORS headers.

Auth Required: Yes Headers: X-Session-Id: <session_id>

Query Parameters:

  • url (required): URL-encoded stream URL

Response: Proxied stream content with CORS headers.


Rate Limiting

Rate limiting protects the API from abuse. Limits are tracked per authenticated user (not per IP) when a valid session or JWT is present, preventing multiple devices on the same network from sharing a single rate-limit bucket.

ScopeLimitKeyNotes
General API (/api/*)1000 req / 15 minSession ID, JWT sub, or IPAdmin sessions are fully exempt
Auth endpoints (/auth/login, /auth/register, /jwt/login)20 req / 15 minIPPre-auth, always IP-based
Email actions (/auth/forgot-password, /auth/resend-verification)5 req / 1 hourIPPrevents email abuse
TV pairing mutations (/tv/pair, /tv/verify, /tv/pairing/confirm)10 req / 5 minIPPrevents PIN brute-force
TV pairing status polling (/tv/pairing/status)120 req / 10 minIP~1 poll every 5 seconds
Health sync (/channels/health-sync)1 per device / 5 minDevice IDIn-memory, per-device
Report status (/channels/:id/report-status)1 per channel per device / 5 minChannel+DeviceIn-memory
Report play (/channels/:id/report-play)1 per channel per device / 1 minChannel+DeviceIn-memory

Standard rate limit headers (RateLimit-*) are included in all responses.


Smart Stream Grouping & Fallback Endpoints

1. Get Grouped Channels (IPTV-org)

GET /api/v1/iptv-org/api/grouped

Returns IPTV-org channels grouped by channelId with ranked streams.

Query Parameters: country, language, languages, category, status, search, limit (default 50), skip (default 0)

Response:

{
  "success": true,
  "count": 1234,
  "data": [{
    "channelId": "IndiaToday.in",
    "channelName": "India Today",
    "tvgLogo": "...",
    "country": "IN",
    "categories": ["news"],
    "streamCount": 3,
    "bestStream": { "streamUrl": "...", "quality": "1080p", "liveness": {...} },
    "streams": [...]
  }]
}

2. Import Grouped Channels

POST /api/v1/iptv-org/import-grouped (Admin)

Import channels with primary stream + alternate streams.

Body:

{
  "channels": [{
    "channelId": "...",
    "channelName": "...",
    "selectedStreamUrl": "...",
    "alternateStreams": [{ "streamUrl": "...", "quality": "720p", "liveness": {...} }],
    "tvgLogo": "...",
    "channelGroup": "...",
    "metadata": {}
  }],
  "replaceExisting": false
}

3. Get Channel with Fallbacks

GET /api/v1/channels/:id/with-fallbacks

Returns channel with only alive, non-flagged alternate streams sorted by ranking.

4. Get User Channels with Fallbacks

GET /api/v1/user-playlist/me/channels-with-fallbacks

Same as /me/channels but includes filtered alternate streams for each channel.

5. Flag Primary Stream

POST /api/v1/channels/:id/flag (Any authenticated user)

Body: { "reason": "looping" | "frozen" | "wrong-content" | "other" }

6. Unflag Primary Stream

POST /api/v1/channels/:id/unflag (Admin only)

7. Flag Alternate Stream

POST /api/v1/channels/:id/alternates/:index/flag (Any authenticated user)

Body: { "reason": "looping" | "frozen" | "wrong-content" | "other" }

8. Unflag Alternate Stream

POST /api/v1/channels/:id/alternates/:index/unflag (Admin only)