Add Category 3: Line API with management operations
This commit is contained in:
@@ -0,0 +1,962 @@
|
||||
# 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:**
|
||||
```bash
|
||||
curl "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=get_line&id=123"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```bash
|
||||
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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```bash
|
||||
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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=delete_line" \
|
||||
-d "id=123"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```bash
|
||||
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:**
|
||||
```bash
|
||||
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:**
|
||||
```bash
|
||||
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:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=unban_line" \
|
||||
-d "id=123"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Examples
|
||||
|
||||
### Python
|
||||
|
||||
```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
|
||||
<?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)
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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)
|
||||
```bash
|
||||
-d "exp_date=2025-12-31"
|
||||
```
|
||||
|
||||
### 2. Unix Timestamp
|
||||
```bash
|
||||
-d "exp_date=1735689600"
|
||||
```
|
||||
|
||||
### 3. Relative Time
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```python
|
||||
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:
|
||||
```bash
|
||||
# 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:
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Related Documentation
|
||||
|
||||
- [GET INFO API - Query Lines](02-get-info.md#1-get-all-subscription-lines)
|
||||
- [Authentication Guide](../authentication.md)
|
||||
- [Error Handling](../error-codes.md)
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Need Help?
|
||||
|
||||
- **GitHub Issues:** [Report problems](https://github.com/worldofiptvcom/xui-one-api-docs/issues)
|
||||
- **Community:** [World of IPTV Forums](https://www.worldofiptv.com)
|
||||
- **Documentation:** [Getting Started Guide](../getting-started.md)
|
||||
+545
@@ -758,6 +758,551 @@ paths:
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
/?action=get_line:
|
||||
get:
|
||||
summary: Get Specific Line Details
|
||||
description: |
|
||||
Retrieves detailed information about a specific subscription line by ID.
|
||||
|
||||
**Purpose:** View detailed information for a single line.
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=get_line&id=123"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ View specific line details
|
||||
- ✅ Check line status before editing
|
||||
- ✅ Verify line configuration
|
||||
- ✅ Audit line settings
|
||||
operationId: getLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [get_line]
|
||||
- name: id
|
||||
in: query
|
||||
required: true
|
||||
description: Line ID
|
||||
schema:
|
||||
type: integer
|
||||
example: 123
|
||||
responses:
|
||||
'200':
|
||||
description: Line details retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessResponse'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/Line'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/?action=create_line:
|
||||
post:
|
||||
summary: Create New Subscription Line
|
||||
description: |
|
||||
Creates a new subscription line (M3U/Xtream Codes account).
|
||||
|
||||
**Purpose:** Add new subscriptions to your panel.
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=create_line" \
|
||||
-d "username=newuser" \
|
||||
-d "password=newpass123" \
|
||||
-d "max_connections=1" \
|
||||
-d "exp_date=2025-12-31" \
|
||||
-d "bouquets_selected[]=1" \
|
||||
-d "bouquets_selected[]=2"
|
||||
```
|
||||
|
||||
**Response Example:**
|
||||
```json
|
||||
{
|
||||
"status": "STATUS_SUCCESS",
|
||||
"data": {
|
||||
"line_id": "456",
|
||||
"username": "newuser",
|
||||
"password": "newpass123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ Create new subscriptions
|
||||
- ✅ Add trial accounts
|
||||
- ✅ Set up reseller lines
|
||||
- ✅ Bulk create accounts
|
||||
operationId: createLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [create_line]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: Username for the line
|
||||
example: "newuser"
|
||||
password:
|
||||
type: string
|
||||
description: Password for the line
|
||||
example: "newpass123"
|
||||
max_connections:
|
||||
type: integer
|
||||
description: Maximum concurrent connections
|
||||
default: 1
|
||||
example: 1
|
||||
exp_date:
|
||||
type: string
|
||||
description: Expiration date (YYYY-MM-DD, Unix timestamp, or relative like "1month", "1year")
|
||||
example: "2025-12-31"
|
||||
is_trial:
|
||||
type: integer
|
||||
description: Trial account (0 = No, 1 = Yes)
|
||||
enum: [0, 1]
|
||||
default: 0
|
||||
example: 0
|
||||
is_restreamer:
|
||||
type: integer
|
||||
description: Reseller status (0 = No, 1 = Yes)
|
||||
enum: [0, 1]
|
||||
default: 0
|
||||
example: 0
|
||||
bouquets_selected:
|
||||
type: array
|
||||
description: Array of bouquet/package IDs to assign
|
||||
items:
|
||||
type: integer
|
||||
example: [1, 2, 3]
|
||||
notes:
|
||||
type: string
|
||||
description: Admin notes
|
||||
example: "Premium customer"
|
||||
contact:
|
||||
type: string
|
||||
description: Contact information
|
||||
example: "customer@example.com"
|
||||
isp_lock:
|
||||
type: string
|
||||
description: Lock to specific IP address
|
||||
example: "192.168.1.100"
|
||||
responses:
|
||||
'200':
|
||||
description: Line created successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
/?action=edit_line:
|
||||
post:
|
||||
summary: Edit Existing Line
|
||||
description: |
|
||||
Modifies an existing subscription line.
|
||||
|
||||
**Purpose:** Update line settings and configuration.
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=edit_line" \
|
||||
-d "id=123" \
|
||||
-d "password=newpassword" \
|
||||
-d "max_connections=2"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ Change passwords
|
||||
- ✅ Update connection limits
|
||||
- ✅ Modify expiration dates
|
||||
- ✅ Change assigned packages
|
||||
- ✅ Update notes
|
||||
operationId: editLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [edit_line]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: Line ID to edit
|
||||
example: 123
|
||||
username:
|
||||
type: string
|
||||
description: New username
|
||||
example: "updateduser"
|
||||
password:
|
||||
type: string
|
||||
description: New password
|
||||
example: "newpass456"
|
||||
max_connections:
|
||||
type: integer
|
||||
description: Maximum concurrent connections
|
||||
example: 2
|
||||
exp_date:
|
||||
type: string
|
||||
description: New expiration date
|
||||
example: "2026-12-31"
|
||||
is_trial:
|
||||
type: integer
|
||||
description: Trial status
|
||||
enum: [0, 1]
|
||||
example: 0
|
||||
bouquets_selected:
|
||||
type: array
|
||||
description: Updated bouquet IDs
|
||||
items:
|
||||
type: integer
|
||||
example: [1, 2, 3, 4]
|
||||
notes:
|
||||
type: string
|
||||
description: Updated notes
|
||||
example: "Extended subscription"
|
||||
responses:
|
||||
'200':
|
||||
description: Line updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/?action=delete_line:
|
||||
post:
|
||||
summary: Delete Line
|
||||
description: |
|
||||
Permanently deletes a subscription line.
|
||||
|
||||
**Purpose:** Remove lines from the panel.
|
||||
|
||||
⚠️ **Warning:** This action cannot be undone!
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=delete_line" \
|
||||
-d "id=123"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ Remove expired subscriptions
|
||||
- ✅ Delete test accounts
|
||||
- ✅ Clean up old lines
|
||||
operationId: deleteLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [delete_line]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: Line ID to delete
|
||||
example: 123
|
||||
responses:
|
||||
'200':
|
||||
description: Line deleted successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/?action=enable_line:
|
||||
post:
|
||||
summary: Enable Line
|
||||
description: |
|
||||
Enables a disabled subscription line.
|
||||
|
||||
**Purpose:** Reactivate suspended lines.
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=enable_line" \
|
||||
-d "id=123"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ Reactivate suspended accounts
|
||||
- ✅ Resume service after payment
|
||||
- ✅ Restore access
|
||||
operationId: enableLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [enable_line]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: Line ID to enable
|
||||
example: 123
|
||||
responses:
|
||||
'200':
|
||||
description: Line enabled successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/?action=disable_line:
|
||||
post:
|
||||
summary: Disable Line
|
||||
description: |
|
||||
Disables an active subscription line without deleting it.
|
||||
|
||||
**Purpose:** Temporarily suspend access.
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=disable_line" \
|
||||
-d "id=123"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ Suspend non-payment accounts
|
||||
- ✅ Temporarily block access
|
||||
- ✅ Pause service
|
||||
operationId: disableLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [disable_line]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: Line ID to disable
|
||||
example: 123
|
||||
responses:
|
||||
'200':
|
||||
description: Line disabled successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/?action=ban_line:
|
||||
post:
|
||||
summary: Ban Line
|
||||
description: |
|
||||
Bans a subscription line, blocking access and preventing reactivation.
|
||||
|
||||
**Purpose:** Permanently block abusive or fraudulent accounts.
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=ban_line" \
|
||||
-d "id=123"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ Block fraudulent accounts
|
||||
- ✅ Prevent abuse
|
||||
- ✅ Enforce terms of service
|
||||
operationId: banLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [ban_line]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: Line ID to ban
|
||||
example: 123
|
||||
responses:
|
||||
'200':
|
||||
description: Line banned successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/?action=unban_line:
|
||||
post:
|
||||
summary: Unban Line
|
||||
description: |
|
||||
Removes ban from a previously banned line.
|
||||
|
||||
**Purpose:** Restore access to banned accounts.
|
||||
|
||||
---
|
||||
|
||||
**Example Request:**
|
||||
```bash
|
||||
curl -X POST "http://your-server.com/cSbuFLhp/?api_key=8D3135D30437F86EAE2FA4A2A8345000&action=unban_line" \
|
||||
-d "id=123"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- ✅ Unban false positives
|
||||
- ✅ Restore access after appeal
|
||||
- ✅ Second chances
|
||||
operationId: unbanLine
|
||||
tags:
|
||||
- Line API
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ApiKey'
|
||||
- name: action
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [unban_line]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: Line ID to unban
|
||||
example: 123
|
||||
responses:
|
||||
'200':
|
||||
description: Line unbanned successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
ApiKeyAuth:
|
||||
|
||||
Reference in New Issue
Block a user