Lumi’s public REST API for automation: read account data, check and buy resources, top up balance. Each product has its own API and its own key.
| Product | Base URL | Docs |
|---|
| Domains | https://scamprojecttest.xyz/api/v1 | Domains |
| Hosting (VPS) | https://panel.scamprojecttest.xyz/hosting/api/v1 | Hosting |
| Proxy | https://panel.scamprojecttest.xyz/proxy/api/v1 | Proxy |
A key for one product works only with that product. Domains, hosting and proxy use separate keys. Keys are issued by an operator — message @lumisup_robot.
Authentication
Every request carries the key in a header:
Authorization: Bearer lumi_domains_XXXXXXXXXXXXXXXXXXXX
Keys are shown once — keep them secret. Only a hash is stored; a key cannot be recovered, only re-issued.
Key types
| Type | Access | Binding |
|---|
reseller | Data of one user only | Bound to a single account |
operator | All users | Unbound; full access |
- Reseller: every request is automatically scoped to its account. A foreign
user_id returns 404 (the existence of other accounts is not revealed).
- Operator: passes
user_id explicitly (body or ?user_id=); required for money operations.
Scopes
A reseller key holds a set of scopes. An operator key holds all of them implicitly.
| Scope | Allows |
|---|
domains:read / vps:read / proxy:read | Reads: lists, statuses, prices, catalog |
domains:write / vps:write / proxy:write | Manage your own objects |
domains:buy / vps:buy / proxy:buy | Buy and renew (debits balance) |
deposits:write | Create top-up invoices |
admin:* | Operator key only; cross-user operations |
A missing scope → 403 forbidden_scope.
Money operations
Endpoints that move money (create invoice, buy, renew) follow extra rules.
Money operations work only when API_MONEY_ENABLED is on for the deployment. While off, such requests return 403 money_disabled even if the key holds the scope.
Idempotency
Every money POST must carry an Idempotency-Key header (8–200 chars):
Idempotency-Key: a1b2c3d4-orders-2026-06-09
- A repeat with the same key returns the original response without re-running the operation.
- Two simultaneous identical requests won’t both execute: the loser gets
409 in_progress.
- Missing header →
400 idempotency_key_required.
Daily spend cap
A key may have a daily USD cap (or the global default). Once reached, money operations return 402 daily_cap_exceeded until the next UTC day. Operator keys are exempt.
Request signing (optional)
If a key is issued with signing required, every money request must carry:
X-Signature: HMAC-SHA256(signing_secret, raw_request_body)
The signature is computed over the exact request-body bytes, so a leaked Bearer alone cannot move money without the second secret. A missing/invalid signature → 401 invalid_signature.
import hmac, hashlib, requests
KEY = "lumi_domains_XXX"
SIGNING = "your_signing_secret" # issued alongside the key when signing is on
body = b'{"amount_usd":"5","provider":"cryptobot"}'
sig = hmac.new(SIGNING.encode(), body, hashlib.sha256).hexdigest()
requests.post(
"https://scamprojecttest.xyz/api/v1/deposits",
headers={
"Authorization": f"Bearer {KEY}",
"Idempotency-Key": "dep-2026-06-09-001",
"X-Signature": sig,
"Content-Type": "application/json",
},
data=body,
)
Rate limits
| Class | Default |
|---|
| Regular requests | 120 req / 60 s per key |
| Money requests | 20 req / 60 s per key |
A bulk request consumes one money “token” per item. On exceed → 429 rate_limited with a Retry-After header (seconds).
List endpoints use a cursor:
GET /v1/domains?limit=50&cursor=eyJ...
limit: 1–200 (default 50).
- Response:
{ "items": [...], "next_cursor": "...", "has_more": true }. Pass next_cursor as cursor for the next page. next_cursor: null means no more pages.
Bulk operations
Bulk endpoints take an array and return a per-item result — they are not atomic: one item failing does not roll back the others.
{
"total": 2,
"ok": 1,
"failed": 1,
"items": [
{ "ref": "a.com", "status": "ok", "result": { "...": "..." } },
{ "ref": "b.com", "status": "failed", "error": { "code": "...", "message": "..." } }
]
}
Item cap per request is 500, else 413 batch_too_large. Long bulk purchases run asynchronously: the endpoint returns 202 with a batch_id; poll status via GET /v1/batches/{batch_id}.
All errors share one JSON shape:
{ "error": { "code": "forbidden_scope", "message": "Key lacks the required scope 'domains:buy'." } }
| HTTP | code | When |
|---|
| 401 | missing_bearer | No Authorization header |
| 401 | invalid_key | Key unknown, revoked, or expired |
| 401 | invalid_signature | Signing required but missing/invalid |
| 402 | daily_cap_exceeded | Daily spend cap reached |
| 403 | forbidden_scope | Key lacks the scope |
| 403 | money_disabled | Money operations disabled on deployment |
| 404 | not_found | Object not found (or foreign, for reseller) |
| 409 | in_progress | An identical request is still running |
| 413 | batch_too_large | Too many items in a bulk request |
| 422 | validation_error | Invalid body/params |
| 429 | rate_limited | Rate limit exceeded |
| 503 | api_disabled | API disabled on deployment |
Money in responses
Money amounts are always strings ("5.15"), never floats — no precision loss. Send amounts as strings too.
Health
GET /healthz → { "status": "ok", "service": "api" }
Unauthenticated liveness probe.