Reference
API
Everything the web panel does goes through a REST API under /api/v1, documented as OpenAPI
3.1. Use it for billing systems, Discord bots, scripts and monitoring.
- API reference: every endpoint, parameter and response, generated from the OpenAPI document.
- Your own panel serves the same reference at
/api/v1/docsand the raw document at/api/v1/openapi.json.
Authentication
There are two ways to authenticate.
API keys (recommended for tools)
Create a key on your account page under API keys (Users and permissions) and send it as a bearer token:
curl -s https://panel.example.com/api/v1/services \
-H "Authorization: Bearer ngcpk_..."
- A key acts as the account that created it, limited by the key’s scopes if it has any.
- Keys do not need CSRF tokens.
- Keys cannot create other keys, change passwords or change two-step verification.
- The admin IP allow-list applies to keys of SuperAdmins and Admins.
- A scoped key reaches services only if its scopes include
services:allorservices:create.
Session cookie (what the browser uses)
-
Sign in:
curl -s -c jar.txt -H "Content-Type: application/json" \ -d '{"login": "alice", "password": "..."}' \ https://panel.example.com/api/v1/auth/loginThe answer is either
{"status": "ok", "csrf_token": "...", "user": {...}}, or, with two-step verification on,{"status": "mfa_required", "mfa_token": "..."}. In the second case send the code within five minutes:curl -s -c jar.txt -H "Content-Type: application/json" \ -d '{"mfa_token": "...", "code": "123456"}' \ https://panel.example.com/api/v1/auth/login/mfa(
"recovery_code"instead of"code"uses a recovery code.) -
The session lives in the
ngcp_sessioncookie (HttpOnly). Every state-changing request (POST, PUT, PATCH, DELETE) must also carry the CSRF token in theX-CSRF-Tokenheader.GET /api/v1/auth/mereturns it again:curl -s -b jar.txt -X POST -H "X-CSRF-Token: <csrf_token>" \ https://panel.example.com/api/v1/services/12/restart -
POST /api/v1/auth/logoutends the session;POST /api/v1/auth/logout-allends all of yours.
Request bodies must be JSON (Content-Type: application/json); the only exception is upload
chunks.
Responses
| Status | Meaning |
|---|---|
| 200 | OK, body follows |
| 201 | Created |
| 202 | Accepted: the node does the work and reports later (backups, restores) |
| 204 | Done, no body |
| 401 | Not signed in, or the key is invalid / expired |
| 403 | Signed in, but not allowed |
| 404 | Not found, or you are not allowed to see it (services and accounts you may not see are “not found”) |
| 409 | Conflict with the current state (wrong status, node offline, in use) |
| 422 | Validation failed |
| 429 | Rate limited; wait for Retry-After |
Errors
Every error uses the same envelope:
{
"error": {
"code": "validation_failed",
"message": "slots must be 2 to 256",
"details": [
{"location": "body.slots", "message": "must be 2 to 256", "value": 500}
]
}
}
code is stable and meant for programs; message is meant for people. Common codes:
| Code | When |
|---|---|
unauthenticated |
No or invalid credentials |
invalid_credentials, invalid_mfa_code |
Sign-in failed |
account_locked, account_inactive |
Lockout, suspended or disabled account |
csrf_failed |
Missing or wrong X-CSRF-Token on a cookie request |
mfa_enrollment_required |
The account must turn on two-step verification first |
ip_not_allowed |
The admin IP allow-list refused the address |
forbidden |
Missing permission |
not_found |
Missing or not visible |
conflict |
See message, for example “the service’s node is offline” |
last_superadmin |
The last active SuperAdmin cannot be changed that way |
validation_failed |
See details |
rate_limited |
Too many requests |
Unknown paths under /api/ always answer with this envelope, never with the web page.
Rate limits
| Who | Limit |
|---|---|
| Sign-in, per IP | 10 attempts, then one every 6 seconds |
| Signed-in callers (session or key) | Bursts of 200, then 20 requests per second |
| Anonymous endpoints, per IP | Bursts of 60, then 10 per second |
Behind a reverse proxy, set NGCP_TRUSTED_PROXIES so limits apply per client, not per proxy.
Common tasks
H='Authorization: Bearer ngcpk_...'
P=https://panel.example.com/api/v1
# Services you can see, with live status
curl -s "$P/services" -H "$H"
# Start / stop / restart / kill / update / reinstall / cancel_install / suspend / unsuspend
curl -s -X POST "$P/services/12/start" -H "$H"
# Send a console line
curl -s -X POST "$P/services/12/console" -H "$H" -H 'Content-Type: application/json' -d '{"line": "say hello"}'
# Create a service
curl -s -X POST "$P/services" -H "$H" -H 'Content-Type: application/json' \
-d '{"name": "Survival", "game_id": "minecraft_java", "node_id": 3, "slots": 20,
"variables": {"accept_eula": "true"}, "start": true}'
# Nodes with status and metrics
curl -s "$P/nodes" -H "$H"
# Audit log
curl -s "$P/audit-events?limit=50" -H "$H"
Automation endpoints: Scheduled tasks, Backups, RCON and player counts, Mod manager, Notifications.
WebSocket
GET /ws upgrades to a WebSocket that carries live service updates and consoles. Browsers
authenticate with the session cookie; tools can send Authorization: Bearer ngcpk_... on the
upgrade request. The Origin, if sent, must be the panel’s own host.
Messages are JSON. Subscribe to topics:
{"type": "subscribe", "topic": "service:12"}
{"type": "subscribe", "topic": "console:12"}
{"type": "unsubscribe", "topic": "console:12"}
{"type": "input", "service_id": 12, "line": "say hello"}
| Topic | Needs | Events |
|---|---|---|
service:<id> |
view |
service (the service with its status, players …), metrics (CPU, memory, processes), backups (a backup or restore changed), deleted |
console:<id> |
console |
console: {"service_id": 12, "lines": [...], "backlog": false}. A batch with backlog: true replaces what you show (sent first, and after an agent reconnect). |
Events look like {"type": "metrics", "topic": "service:12", "data": {...}}. Control answers are
{"type": "subscribed" | "unsubscribed" | "error", "topic": "...", "message": "..."}.
- One socket can hold up to 64 topics.
inputlines are console input and are audited. - The server pings every 25 seconds and re-checks your session every 30 seconds; signing out closes the socket with code 4001.
- A client that cannot keep up is disconnected (code 1013); reconnect and subscribe again.
Versioning
The API path carries the major version (/api/v1). NGCP is pre-1.0: endpoints may still be added
or changed between releases; check the changelog. GET /api/v1/version returns
the controller’s build version.