API Authentication¶
The enconf REST API provides programmatic access to all hosting features. This page describes the available authentication methods, the token format and rate limiting.
Base URL¶
All API endpoints are available at the following base URL:
Port 3443
The panel runs on port 3443 by default. The API is accessible on the same port.
Interactive API Documentation¶
A complete, interactive API reference with all endpoints, parameters and example responses is available directly in your panel:
You can test all API calls directly in your browser (Swagger UI). Authenticate via the Authorize button using your API key or JWT token.
For WHMCS / HostBill developers
The OpenAPI specification (YAML) is available at https://your-panel.com:3443/api/docs/openapi.yaml and can be imported directly into your development environment.
Authentication Methods¶
The API supports two authentication methods:
| Method | Use Case | Validity |
|---|---|---|
| JWT Token | Interactive use, frontend | Configurable (default: 24 hours) |
| API Key | Automation, scripts, integrations | Until expiration date or manual deletion |
JWT Token (Login)¶
For interactive use, authenticate with email and password to receive a JWT token.
Login Request¶
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "customer@example.com",
"password": "YourPassword"
}
Successful Response¶
{
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": 42,
"email": "customer@example.com",
"name": "John Doe",
"role": "customer"
}
},
"error": null,
"message": "Login successful"
}
Login with 2FA¶
If two-factor authentication is enabled, the login additionally requires a TOTP code:
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "customer@example.com",
"password": "YourPassword",
"totp_code": "123456"
}
If the TOTP code is missing and 2FA is enabled, the API responds with:
Using the Token¶
Send the token in the Authorization header with all subsequent requests:
Token Validity¶
| Setting | Default Value |
|---|---|
| Validity Period | 24 hours (configurable by admin) |
| Algorithm | HS256 (HMAC-SHA256) |
After the token expires, you must log in again.
API Keys¶
API keys are ideal for automation, deployment scripts and integrations with third-party systems.
Create API Key¶
POST /api/v1/auth/api-keys
Authorization: Bearer <jwt-token>
Content-Type: application/json
{
"name": "Deployment Script",
"expires_at": "2027-12-31T23:59:59Z"
}
| Field | Required | Description |
|---|---|---|
name |
Yes | Label (1–100 characters) |
expires_at |
No | Expiration date in RFC 3339 format |
Response¶
{
"data": {
"id": 1,
"name": "Deployment Script",
"key": "ncp_a1b2c3d4e5f6...",
"key_prefix": "ncp_a1b2c3d4e5f6",
"expires_at": "2027-12-31T23:59:59Z",
"created_at": "2026-03-31T10:00:00Z"
},
"error": null,
"message": "API key created"
}
Key Visible Only Once
The key field is returned only in this response. Store the key securely immediately. Only a SHA-256 hash of the key is stored in the database — the plaintext cannot be recovered.
Using an API Key¶
Send the key in the Authorization header:
List API Keys¶
Response:
{
"data": [
{
"id": 1,
"name": "Deployment Script",
"key_prefix": "ncp_a1b2c3d4e5f6",
"last_used_at": "2026-03-30T14:22:00Z",
"expires_at": "2027-12-31T23:59:59Z",
"created_at": "2026-03-31T10:00:00Z"
}
],
"error": null,
"message": ""
}
key_prefix
The key_prefix shows the first 20 characters of the key and serves for identification. The full key is never displayed again.
Delete API Key¶
Key Format¶
API keys use the following format:
- Prefix:
ncp_(NetCell Panel) - 32 bytes of random data, hex-encoded
- Total length: 68 characters
Response Format¶
All API responses use a consistent JSON format:
| Field | Type | Description |
|---|---|---|
data |
Object/Array/null | The requested data |
error |
String/null | Error description (null on success) |
message |
String | Human-readable message |
Error Responses¶
| HTTP Status | Meaning |
|---|---|
400 |
Bad request (missing or invalid parameters) |
401 |
Not authenticated (token missing or invalid) |
403 |
Forbidden (insufficient role or resource belongs to another customer) |
404 |
Resource not found |
409 |
Conflict (e.g. duplicate) |
422 |
Validation error |
429 |
Too many requests (rate limit exceeded) |
500 |
Internal server error |
Example error response:
Rate Limiting¶
The API limits the number of requests per time period to prevent abuse:
| Endpoint | Limit |
|---|---|
Login (/auth/login) |
10 requests per minute per IP |
| Password Reset | 5 requests per minute per IP |
| All Other Endpoints | 120 requests per minute per user |
When the rate limit is exceeded, the API responds with HTTP status 429:
{
"data": null,
"error": "rate limit exceeded",
"message": "Too many requests. Please try again later."
}
Rate Limit Headers
The API sends the following headers with every response:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per time window |
X-RateLimit-Remaining |
Remaining requests |
X-RateLimit-Reset |
Unix timestamp when the limit resets |
Subscription Context¶
Customer endpoints require the context of a subscription. Pass the subscription ID as a query parameter:
If the parameter is missing and the customer has multiple subscriptions, the default subscription is used.
Examples¶
cURL¶
# Login
TOKEN=$(curl -s -X POST https://panel.example.com:3443/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"customer@example.com","password":"secret"}' \
| jq -r '.data.token')
# List websites
curl -s https://panel.example.com:3443/api/v1/sites?subscription_id=1 \
-H "Authorization: Bearer $TOKEN" | jq
# With API key
curl -s https://panel.example.com:3443/api/v1/sites?subscription_id=1 \
-H "Authorization: Bearer ncp_a1b2c3d4..." | jq
Python¶
import requests
BASE_URL = "https://panel.example.com:3443/api/v1"
# Login
resp = requests.post(f"{BASE_URL}/auth/login", json={
"email": "customer@example.com",
"password": "secret"
})
token = resp.json()["data"]["token"]
headers = {"Authorization": f"Bearer {token}"}
# List websites
sites = requests.get(f"{BASE_URL}/sites",
headers=headers,
params={"subscription_id": 1}
)
print(sites.json())
PHP¶
<?php
$baseUrl = 'https://panel.example.com:3443/api/v1';
$apiKey = 'ncp_a1b2c3d4...';
$ch = curl_init("$baseUrl/sites?subscription_id=1");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $apiKey",
"Content-Type: application/json",
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
print_r($data);