Base URL: https://backend-netmindweb.onrender.com/api
Authentication: sign in with POST /auth/login and send the returned token as Authorization: Bearer <token>. Access tokens expire after a short time — use /auth/refresh to get a new one.
Content-Type: request bodies are application/json. IDs are UUIDs.
Limits: sign-in endpoints allow 20 requests per 15 minutes per IP; other endpoints 600. Browser calls are only accepted from the NetMind website — call the API from your own server or scripts.
Authentication
POST
/auth/signup
Create an account
{
"email": "you@company.com",
"password": "at least 8 characters",
"full_name": "Your Name",
"company_name": "Optional"
}
// 201 — no tokens yet: the email address must be verified first
{ "message": "Account created successfully. Please check your email…", "requiresVerification": true, "email": "you@company.com" }
POST
/auth/login
Sign in with email and password
{ "email": "you@company.com", "password": "…", "remember_me": false }
// 200 — signed in
{
"token": "eyJ…", // access token, send as "Authorization: Bearer <token>"
"token_expires_at": 1791460000000,
"refresh_token": "…",
"refresh_expires_at": 1792060000000,
"user": { "id": "8b1c…-uuid", "email": "you@company.com", "full_name": "Your Name", … },
"subscription": { "plan_id": "free", "status": "active", "renewal_date": null }
}
// 200 — two-factor authentication is on: finish with /auth/2fa/challenge
{ "requires_2fa": true, "mfa_token": "eyJ…" }
// 401 wrong password · 403 EMAIL_NOT_VERIFIED · 429 ACCOUNT_LOCKED (after 5 failures)
POST
/auth/2fa/challenge
Second step when 2FA is on
{ "mfa_token": "eyJ…", "code": "123456" }
// 200 — same body as a successful /auth/login
// 401 INVALID_2FA (counts toward the lockout) · 401 MFA_EXPIRED (sign in again; tokens last 5 minutes)
POST
/auth/refresh
Get a new access token
{ "refresh_token": "…" }
// 200 — a new token pair; the old refresh token stops working
{ "token": "eyJ…", "token_expires_at": …, "refresh_token": "…", "refresh_expires_at": … }
POST
/auth/logout
Revoke a refresh token
{ "refresh_token": "…" }
// 200
{ "message": "Logged out successfully." }
Account
GET
/user/profile
Your profile and preferences
Auth required
// 200
{
"user": {
"id": "8b1c…-uuid", "email": "you@company.com", "full_name": "Your Name",
"company_name": "Acme", "phone": "+216 20 123 456", "two_factor_enabled": false,
"email_daily_report": true, "email_weekly_summary": true, "timezone": "Africa/Tunis",
"theme": "light", "webhook_url": null, "created_at": "2026-10-08T17:42:38.027Z"
}
}
PUT
/user/profile
Update name, company or phone
Auth required
{ "full_name": "New Name", "company_name": "New Co", "phone": "+216 20 123 456" }
// 200
{ "message": "Profile updated successfully.", "user": { … } }
PUT
/user/preferences
Partial update of preferences
Auth required
// Send only the fields you want to change. "webhook_url": "" removes the webhook.
{ "email_weekly_summary": false, "timezone": "Africa/Tunis", "theme": "dark", "webhook_url": "https://example.org/hook" }
// 200
{ "message": "Preferences saved successfully." }
GET
/user/activity?limit=20&type=login
Activity and alerts (newest first, max 50)
Auth required
// 200
{ "activity": [ { "id": 42, "type": "device_new", "message": "New device joined your network: Sam iPhone (192.168.1.21)",
"severity": "warning", "icon": "🔌", "metadata": { "ip": "192.168.1.21", … }, "created_at": "…" } ] }
GET
/user/export
Download all your data as JSON
Auth required
// 200 — account, preferences, subscription, devices, bandwidth_history, invoices, sessions, activity
GET
/subscription
Current plan
Auth required
// 200
{
"subscription": { "plan_id": "paid", "status": "active", "renewal_date": "2026-11-08T…", "cancel_at_period_end": false,
"max_devices": 20, "history_days": 30, … },
"billing": { "online_payments": false, "paid_price_cents": 2000, "currency": "usd" }
}
Network data (from the desktop app)
GET
/tool/devices
Devices from the latest scan
Auth required
// 200 — status is "live" while the app is online, "stale" otherwise, "not_configured" if never linked
{
"status": "live", "total": 2, "last_updated": "…",
"devices": [ { "ip": "192.168.1.20", "mac": "aa:bb:cc:00:00:01", "name": "Reception PC",
"status": "active", "download": 12.5, "upload": 1.2 } ] // speeds in Mbps
}
GET
/tool/status
Is the desktop app online?
Auth required
// 200
{ "status": "online", "last_updated": "…", "online_since": "…", "uptime_seconds": 5400, "device_count": 7, … }
// or { "status": "offline", "last_seen": "…" } · { "status": "not_configured" }
GET
/tool/history?hours=24
Bandwidth history (capped at 7 days Free / 30 days Paid)
Auth required
// 200
{ "hours": 24, "max_hours": 168, "bucket_seconds": 240, "plan": "free",
"points": [ { "t": "2026-10-08T17:00:00Z", "down_mbps": 14.2, "up_mbps": 1.6, "device_count": 7 } ] }
POST
/tool/command
Block or limit a device remotely
Auth required
{ "command": "limit", "payload": { "ip": "192.168.1.45", "down_kbps": 625, "up_kbps": 312 } } // KB/s
{ "command": "block", "payload": { "ip": "192.168.1.45" } }
{ "command": "remove_limit", "payload": { "ip": "192.168.1.45" } }
{ "command": "restore_all", "payload": {} }
// 200 { "ok": true, "command_id": "…", "message": "Command 'limit' queued…" }
// 403 DEVICE_LIMIT (plan cap reached) · 503 desktop app offline
Reviews (public)
GET
/reviews
Approved reviews
// 200
{ "reviews": [ { "id": "c1f2…-uuid", "name": "Sam", "rating": 5, "comment": "…", "created_at": "…" } ] }
POST
/reviews
Submit a review (3 per hour per IP)
{ "name": "Sam", "rating": 5, "comment": "Setup was easy and the till is fast again." }
// 201
{ "message": "Review submitted — it will appear after approval" }