Files
xui-one-api-docs/docs/categories/03-line-api.md
T

24 KiB
Raw Blame History

Line API - Subscription Management

Complete documentation for subscription line management in the XUI.ONE Admin API.

📋 Overview

The Line API provides complete control over subscription lines (M3U/Xtream Codes accounts). You can create, modify, enable, disable, and delete subscription lines programmatically.

Available Operations

Operation Action Method Description
View get_line GET Get specific line details
Create create_line POST Create new subscription
Update edit_line POST Modify existing line
Delete delete_line POST Permanently remove line
Enable enable_line POST Activate disabled line
Disable disable_line POST Temporarily suspend line
Ban ban_line POST Block abusive account
Unban unban_line POST Remove ban from line

🔍 Get Specific Line

Action: get_line

Retrieve detailed information about a specific subscription line.

Parameters:

  • id (required) - Line ID

Request:

curl "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=get_line&id=123"

Response:

{
  "status": "STATUS_SUCCESS",
  "data": {
    "id": "123",
    "username": "testuser",
    "password": "testpass123",
    "exp_date": "1735689600",
    "max_connections": "1",
    "is_trial": "0",
    "enabled": "1",
    "is_restreamer": "0",
    "bouquet": "[1,2,3]",
    "notes": "Premium customer",
    "created_at": "1609459200"
  }
}

Create New Line

Action: create_line

Create a new subscription line with specified settings.

Required Parameters:

  • username - Username for the line
  • password - Password for the line

Optional Parameters:

  • max_connections - Maximum concurrent connections (default: 1)
  • exp_date - Expiration date (YYYY-MM-DD, timestamp, or relative like "1month")
  • is_trial - Trial status (0 = No, 1 = Yes)
  • is_restreamer - Reseller status (0 = No, 1 = Yes)
  • bouquets_selected[] - Array of bouquet/package IDs
  • notes - Admin notes
  • contact - Contact information
  • isp_lock - Lock to specific IP address

Request:

curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=create_line" \
  -d "username=newuser" \
  -d "password=securepass123" \
  -d "max_connections=1" \
  -d "exp_date=2025-12-31" \
  -d "bouquets_selected[]=1" \
  -d "bouquets_selected[]=2" \
  -d "notes=Premium customer"

Response:

{
  "status": "STATUS_SUCCESS",
  "data": {
    "line_id": "456",
    "username": "newuser",
    "password": "securepass123"
  }
}

✏️ Edit Existing Line

Action: edit_line

Modify settings of an existing subscription line.

Required Parameters:

  • id - Line ID to edit

Optional Parameters:

  • username - New username
  • password - New password
  • max_connections - Update connection limit
  • exp_date - Update expiration date
  • is_trial - Update trial status
  • bouquets_selected[] - Update assigned packages
  • notes - Update notes

Request:

curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=edit_line" \
  -d "id=123" \
  -d "password=newpassword456" \
  -d "max_connections=2" \
  -d "exp_date=2026-06-30"

Response:

{
  "status": "STATUS_SUCCESS",
  "data": {
    "message": "Line updated successfully"
  }
}

🗑️ Delete Line

Action: delete_line

Permanently remove a subscription line from the panel.

⚠️ Warning: This action cannot be undone!

Required Parameters:

  • id - Line ID to delete

Request:

curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=delete_line" \
  -d "id=123"

Response:

{
  "status": "STATUS_SUCCESS",
  "data": {
    "message": "Line deleted successfully"
  }
}

Enable Line

Action: enable_line

Activate a previously disabled line.

Required Parameters:

  • id - Line ID to enable

Request:

curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=enable_line" \
  -d "id=123"

Disable Line

Action: disable_line

Temporarily suspend a line without deleting it.

Required Parameters:

  • id - Line ID to disable

Request:

curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=disable_line" \
  -d "id=123"

🚫 Ban Line

Action: ban_line

Permanently block an abusive or fraudulent account.

Required Parameters:

  • id - Line ID to ban

Request:

curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=ban_line" \
  -d "id=123"

✓ Unban Line

Action: unban_line

Remove ban from a previously banned line.

Required Parameters:

  • id - Line ID to unban

Request:

curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=unban_line" \
  -d "id=123"

💻 Code Examples

Python

import requests
from datetime import datetime, timedelta

class XUILineManager:
    def __init__(self, base_url, api_key):
        self.base_url = base_url
        self.api_key = api_key
    
    def _make_request(self, action, data=None):
        """Make API request"""
        params = {
            "api_key": self.api_key,
            "action": action
        }
        
        if data:
            response = requests.post(self.base_url, params=params, data=data)
        else:
            response = requests.get(self.base_url, params=params)
        
        result = response.json()
        if result["status"] != "STATUS_SUCCESS":
            raise Exception(f"API Error: {result.get('error', 'Unknown error')}")
        
        return result["data"]
    
    def get_line(self, line_id):
        """Get specific line details"""
        return self._make_request("get_line", {"id": line_id})
    
    def create_line(self, username, password, **kwargs):
        """Create new subscription line"""
        data = {
            "username": username,
            "password": password,
            **kwargs
        }
        return self._make_request("create_line", data)
    
    def edit_line(self, line_id, **kwargs):
        """Edit existing line"""
        data = {"id": line_id, **kwargs}
        return self._make_request("edit_line", data)
    
    def delete_line(self, line_id):
        """Delete line"""
        return self._make_request("delete_line", {"id": line_id})
    
    def enable_line(self, line_id):
        """Enable line"""
        return self._make_request("enable_line", {"id": line_id})
    
    def disable_line(self, line_id):
        """Disable line"""
        return self._make_request("disable_line", {"id": line_id})
    
    def ban_line(self, line_id):
        """Ban line"""
        return self._make_request("ban_line", {"id": line_id})
    
    def unban_line(self, line_id):
        """Unban line"""
        return self._make_request("unban_line", {"id": line_id})

# Usage Example
manager = XUILineManager(
    base_url="http://your-server.com/cSbuFLhp/",
    api_key="8D3135D30437F86EAE2FA4A2A8345000"
)

# Create new line
new_line = manager.create_line(
    username="customer001",
    password="secure123",
    max_connections=1,
    exp_date="2025-12-31",
    bouquets_selected=[1, 2, 3],
    notes="Premium customer"
)
print(f"Created line ID: {new_line['line_id']}")

# Get line details
line = manager.get_line(123)
print(f"Line: {line['username']} - Expires: {line['exp_date']}")

# Edit line
manager.edit_line(
    line_id=123,
    password="newpassword",
    max_connections=2
)
print("Line updated successfully")

# Disable line
manager.disable_line(123)
print("Line disabled")

PHP

<?php
class XUILineManager {
    private $baseUrl;
    private $apiKey;
    
    public function __construct($baseUrl, $apiKey) {
        $this->baseUrl = $baseUrl;
        $this->apiKey = $apiKey;
    }
    
    private function makeRequest($action, $data = null) {
        $url = $this->baseUrl . "?api_key=" . $this->apiKey . "&action=" . $action;
        
        if ($data) {
            $options = [
                'http' => [
                    'method' => 'POST',
                    'header' => 'Content-type: application/x-www-form-urlencoded',
                    'content' => http_build_query($data)
                ]
            ];
            $context = stream_context_create($options);
            $response = file_get_contents($url, false, $context);
        } else {
            $response = file_get_contents($url);
        }
        
        $result = json_decode($response, true);
        
        if ($result['status'] !== 'STATUS_SUCCESS') {
            throw new Exception("API Error: " . ($result['error'] ?? 'Unknown error'));
        }
        
        return $result['data'];
    }
    
    public function getLine($lineId) {
        return $this->makeRequest("get_line", ["id" => $lineId]);
    }
    
    public function createLine($username, $password, $options = []) {
        $data = array_merge([
            "username" => $username,
            "password" => $password
        ], $options);
        
        return $this->makeRequest("create_line", $data);
    }
    
    public function editLine($lineId, $updates) {
        $data = array_merge(["id" => $lineId], $updates);
        return $this->makeRequest("edit_line", $data);
    }
    
    public function deleteLine($lineId) {
        return $this->makeRequest("delete_line", ["id" => $lineId]);
    }
    
    public function enableLine($lineId) {
        return $this->makeRequest("enable_line", ["id" => $lineId]);
    }
    
    public function disableLine($lineId) {
        return $this->makeRequest("disable_line", ["id" => $lineId]);
    }
}

// Usage Example
$manager = new XUILineManager(
    "http://your-server.com/cSbuFLhp/",
    "8D3135D30437F86EAE2FA4A2A8345000"
);

// Create new line
$newLine = $manager->createLine(
    "customer001",
    "secure123",
    [
        "max_connections" => 1,
        "exp_date" => "2025-12-31",
        "notes" => "Premium customer"
    ]
);
echo "Created line ID: " . $newLine['line_id'] . "\n";

// Edit line
$manager->editLine(123, [
    "password" => "newpassword",
    "max_connections" => 2
]);
echo "Line updated successfully\n";
?>

JavaScript (Node.js)

const fetch = require('node-fetch');

class XUILineManager {
    constructor(baseUrl, apiKey) {
        this.baseUrl = baseUrl;
        this.apiKey = apiKey;
    }
    
    async makeRequest(action, data = null) {
        const url = `${this.baseUrl}?api_key=${this.apiKey}&action=${action}`;
        
        const options = {
            method: data ? 'POST' : 'GET',
        };
        
        if (data) {
            const formData = new URLSearchParams();
            for (const [key, value] of Object.entries(data)) {
                if (Array.isArray(value)) {
                    value.forEach(v => formData.append(`${key}[]`, v));
                } else {
                    formData.append(key, value);
                }
            }
            options.body = formData;
        }
        
        const response = await fetch(url, options);
        const result = await response.json();
        
        if (result.status !== 'STATUS_SUCCESS') {
            throw new Error(`API Error: ${result.error || 'Unknown error'}`);
        }
        
        return result.data;
    }
    
    async getLine(lineId) {
        const url = `${this.baseUrl}?api_key=${this.apiKey}&action=get_line&id=${lineId}`;
        const response = await fetch(url);
        const result = await response.json();
        
        if (result.status !== 'STATUS_SUCCESS') {
            throw new Error(`API Error: ${result.error || 'Unknown error'}`);
        }
        
        return result.data;
    }
    
    async createLine(username, password, options = {}) {
        const data = { username, password, ...options };
        return this.makeRequest('create_line', data);
    }
    
    async editLine(lineId, updates) {
        const data = { id: lineId, ...updates };
        return this.makeRequest('edit_line', data);
    }
    
    async deleteLine(lineId) {
        return this.makeRequest('delete_line', { id: lineId });
    }
    
    async enableLine(lineId) {
        return this.makeRequest('enable_line', { id: lineId });
    }
    
    async disableLine(lineId) {
        return this.makeRequest('disable_line', { id: lineId });
    }
    
    async banLine(lineId) {
        return this.makeRequest('ban_line', { id: lineId });
    }
    
    async unbanLine(lineId) {
        return this.makeRequest('unban_line', { id: lineId });
    }
}

// Usage Example
(async () => {
    const manager = new XUILineManager(
        'http://your-server.com/cSbuFLhp/',
        '8D3135D30437F86EAE2FA4A2A8345000'
    );
    
    try {
        // Create new line
        const newLine = await manager.createLine(
            'customer001',
            'secure123',
            {
                max_connections: 1,
                exp_date: '2025-12-31',
                bouquets_selected: [1, 2, 3],
                notes: 'Premium customer'
            }
        );
        console.log('Created line ID:', newLine.line_id);
        
        // Get line details
        const line = await manager.getLine(123);
        console.log('Line:', line.username, '- Expires:', line.exp_date);
        
        // Edit line
        await manager.editLine(123, {
            password: 'newpassword',
            max_connections: 2
        });
        console.log('Line updated successfully');
        
    } catch (error) {
        console.error('Error:', error.message);
    }
})();

🎯 Common Use Cases

1. Bulk Create Lines

def bulk_create_lines(manager, count=10, package_ids=[1,2,3]):
    """Create multiple lines at once"""
    import random
    import string
    
    created_lines = []
    
    for i in range(count):
        # Generate random credentials
        username = f"user{''.join(random.choices(string.digits, k=6))}"
        password = ''.join(random.choices(string.ascii_letters + string.digits, k=12))
        
        try:
            result = manager.create_line(
                username=username,
                password=password,
                max_connections=1,
                exp_date="1month",  # 1 month from now
                bouquets_selected=package_ids
            )
            
            created_lines.append({
                'id': result['line_id'],
                'username': username,
                'password': password
            })
            
            print(f"✓ Created: {username}")
            
        except Exception as e:
            print(f"✗ Failed to create line {i+1}: {e}")
    
    return created_lines

2. Auto-Disable Expired Lines

from datetime import datetime

def disable_expired_lines(manager):
    """Automatically disable lines that have expired"""
    # Get all lines
    response = requests.get(
        manager.base_url,
        params={"api_key": manager.api_key, "action": "get_lines"}
    )
    lines = response.json()['data']
    
    now = datetime.now().timestamp()
    expired_count = 0
    
    for line in lines:
        exp_date = int(line['exp_date'])
        
        # Check if expired and still enabled
        if exp_date < now and line['enabled'] == '1':
            try:
                manager.disable_line(line['id'])
                print(f"✓ Disabled expired line: {line['username']}")
                expired_count += 1
            except Exception as e:
                print(f"✗ Failed to disable {line['username']}: {e}")
    
    print(f"\nDisabled {expired_count} expired lines")

3. Reset Password for Line

import secrets
import string

def reset_line_password(manager, line_id):
    """Generate and set a new secure password"""
    # Generate secure password
    alphabet = string.ascii_letters + string.digits
    new_password = ''.join(secrets.choice(alphabet) for i in range(16))
    
    # Update line
    manager.edit_line(line_id, password=new_password)
    
    return new_password

# Usage
new_pass = reset_line_password(manager, 123)
print(f"New password: {new_pass}")

4. Extend Expiration Date

from datetime import datetime, timedelta

def extend_subscription(manager, line_id, months=1):
    """Extend line expiration by specified months"""
    # Get current line
    line = manager.get_line(line_id)
    
    # Calculate new expiration
    current_exp = datetime.fromtimestamp(int(line['exp_date']))
    new_exp = current_exp + timedelta(days=30 * months)
    new_exp_timestamp = int(new_exp.timestamp())
    
    # Update line
    manager.edit_line(line_id, exp_date=new_exp_timestamp)
    
    print(f"Extended to: {new_exp.strftime('%Y-%m-%d')}")

5. Upgrade Line to Different Package

def upgrade_line_package(manager, line_id, new_package_ids):
    """Change line's assigned packages"""
    manager.edit_line(
        line_id,
        bouquets_selected=new_package_ids
    )
    
    print(f"Upgraded line {line_id} to packages: {new_package_ids}")

# Usage
upgrade_line_package(manager, 123, [1, 2, 3, 4, 5])  # Premium package

6. Export Line Credentials

import csv

def export_lines_to_csv(manager, filename="lines_export.csv"):
    """Export all lines to CSV file"""
    # Get all lines
    response = requests.get(
        manager.base_url,
        params={"api_key": manager.api_key, "action": "get_lines"}
    )
    lines = response.json()['data']
    
    # Write to CSV
    with open(filename, 'w', newline='') as csvfile:
        fieldnames = ['id', 'username', 'password', 'exp_date', 'max_connections', 'enabled']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        
        writer.writeheader()
        for line in lines:
            writer.writerow({
                'id': line['id'],
                'username': line['username'],
                'password': line['password'],
                'exp_date': datetime.fromtimestamp(int(line['exp_date'])).strftime('%Y-%m-%d'),
                'max_connections': line['max_connections'],
                'enabled': 'Yes' if line['enabled'] == '1' else 'No'
            })
    
    print(f"Exported {len(lines)} lines to {filename}")

📊 Expiration Date Formats

XUI.ONE accepts multiple formats for expiration dates:

1. Specific Date (YYYY-MM-DD)

-d "exp_date=2025-12-31"

2. Unix Timestamp

-d "exp_date=1735689600"

3. Relative Time

# Hours
-d "exp_date=24hours"

# Days
-d "exp_date=7days"

# Months
-d "exp_date=1month"
-d "exp_date=3months"

# Years
-d "exp_date=1year"

Python Helper Function

from datetime import datetime, timedelta

def format_exp_date(days=None, months=None, years=None, date=None):
    """Convert various formats to Unix timestamp"""
    if date:
        # Specific date
        dt = datetime.strptime(date, '%Y-%m-%d')
    else:
        # Relative date
        dt = datetime.now()
        if days:
            dt += timedelta(days=days)
        if months:
            dt += timedelta(days=30 * months)
        if years:
            dt += timedelta(days=365 * years)
    
    return int(dt.timestamp())

# Usage
exp_date = format_exp_date(months=3)  # 3 months from now

⚠️ Important Notes

Username/Password Requirements

  • Username: 3-50 characters, alphanumeric
  • Password: Minimum 6 characters recommended
  • No special characters in username (use only letters, numbers, underscore)

Bouquet Selection

When assigning bouquets, use array notation:

# cURL
-d "bouquets_selected[]=1" \
-d "bouquets_selected[]=2"

# Python
bouquets_selected=[1, 2, 3]

# PHP
["bouquets_selected" => [1, 2, 3]]

Max Connections

  • Minimum: 1
  • Maximum: Unlimited (but reasonable limits recommended)
  • Common values: 1-5 for home users, 10-50 for resellers

Trial vs. Regular Lines

Trial lines can be automatically converted to regular lines:

# Create trial
manager.create_line(
    username="trial001",
    password="temp123",
    is_trial=1,
    exp_date="7days"
)

# Convert to regular (when customer pays)
manager.edit_line(
    line_id=123,
    is_trial=0,
    exp_date="1year"
)

💡 Best Practices

1. Always Check Before Delete

def safe_delete_line(manager, line_id):
    """Delete line with confirmation"""
    try:
        # Get line details first
        line = manager.get_line(line_id)
        
        # Confirm it's the right line
        print(f"About to delete: {line['username']}")
        confirm = input("Are you sure? (yes/no): ")
        
        if confirm.lower() == 'yes':
            manager.delete_line(line_id)
            print("Line deleted successfully")
        else:
            print("Deletion cancelled")
            
    except Exception as e:
        print(f"Error: {e}")

2. Validate Input Data

def validate_line_data(username, password, max_connections):
    """Validate line data before creation"""
    errors = []
    
    if len(username) < 3 or len(username) > 50:
        errors.append("Username must be 3-50 characters")
    
    if not username.replace('_', '').isalnum():
        errors.append("Username must be alphanumeric")
    
    if len(password) < 6:
        errors.append("Password must be at least 6 characters")
    
    if max_connections < 1:
        errors.append("Max connections must be at least 1")
    
    return errors

# Usage
errors = validate_line_data("user123", "pass", 1)
if errors:
    print("Validation errors:")
    for error in errors:
        print(f"  - {error}")
else:
    # Create line
    manager.create_line("user123", "secure123", max_connections=1)

3. Implement Rate Limiting

import time
from functools import wraps

def rate_limit(calls_per_second=2):
    """Limit API calls to prevent overload"""
    min_interval = 1.0 / calls_per_second
    last_called = [0.0]
    
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            elapsed = time.time() - last_called[0]
            left_to_wait = min_interval - elapsed
            
            if left_to_wait > 0:
                time.sleep(left_to_wait)
            
            result = func(*args, **kwargs)
            last_called[0] = time.time()
            return result
        return wrapper
    return decorator

# Apply to methods
XUILineManager.create_line = rate_limit()(XUILineManager.create_line)

4. Error Handling

def create_line_with_retry(manager, username, password, retries=3):
    """Create line with automatic retry on failure"""
    for attempt in range(retries):
        try:
            return manager.create_line(username, password)
        except Exception as e:
            if attempt < retries - 1:
                print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
                time.sleep(2 ** attempt)  # Exponential backoff
            else:
                print(f"All {retries} attempts failed")
                raise


🆘 Need Help?