{ "openapi": "3.1.0", "info": { "title": "Scanopy API", "description": "\nNetwork topology discovery and visualization API.\n\n## Authentication\n\nTwo authentication methods are supported:\n\n| Method | Header | Use Case |\n|--------|--------|----------|\n| User API key | `Authorization: Bearer scp_u_...` | Programmatic access, integrations |\n| Session cookie | `Cookie: session_id=...` | Web UI (via `/api/auth/login`) |\n\nUser API keys require your organization to have API access enabled. Create keys at **Platform > API Keys**.\n\n## Rate Limiting\n\nLimit: 300 requests/minute\n\nBurst: 150\n\nResponse headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`\n\nWhen rate limited, you'll receive HTTP `429 Too Many Requests` with a `Retry-After` header.\n\n## Pagination\n\nList endpoints support pagination via query parameters:\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `limit` | integer | 50 | Maximum results to return (1-1000). Use 0 for no limit. |\n| `offset` | integer | 0 | Number of results to skip |\n\nExample: `GET /api/v1/hosts?limit=10&offset=20`\n\n## Response Format\n\nAll responses use a standard envelope:\n\n```json\n{\n \"success\": true,\n \"data\": { ... },\n \"meta\": {\n \"api_version\": 1,\n \"server_version\": \"0.16.0\"\n }\n}\n```\n\n**Paginated list responses** include pagination metadata:\n\n```json\n{\n \"success\": true,\n \"data\": [ ... ],\n \"meta\": {\n \"api_version\": 1,\n \"server_version\": \"0.16.0\",\n \"pagination\": {\n \"total_count\": 142,\n \"limit\": 50,\n \"offset\": 0,\n \"has_more\": true\n }\n }\n}\n```\n\n| Field | Description |\n|-------|-------------|\n| `total_count` | Total items matching your query (ignoring pagination) |\n| `limit` | Applied limit (your request or default) |\n| `offset` | Applied offset |\n| `has_more` | `true` if more results exist beyond this page |\n\n**Error responses** include an `error` field instead of `data`:\n\n```json\n{\n \"success\": false,\n \"error\": \"Resource not found\",\n \"meta\": { ... }\n}\n```\n\n**Common status codes:** `400` validation error, `401` unauthorized, `403` forbidden, `404` not found, `409` conflict, `429` rate limited.\n\n## Versioning\n\nEndpoints are prefixed with `/api/v1/`. The API version is an integer (`api_version: 1`) returned in every response, versioned independently from the application. Check `GET /api/version` for current versions.\n\n**While Scanopy is pre-v1.0 (current: 0.16.0)**, the API should be considered unstable. Breaking changes may be introduced in any release without incrementing the API version. We recommend pinning to a specific Scanopy release if you depend on API stability, and reviewing the [changelog](/changelog) before upgrading. After Scanopy reaches v1.0, breaking API changes will only occur with an API version increment.\n\n## Multi-Tenancy\n\nResources are scoped to your **organization** and **network(s)**:\n\n- You can only access entities within your organization\n- Network-level entities (hosts, services, etc.) are filtered to networks you have access to\n- Use `?network_id=` to filter list endpoints to a specific network\n- API keys can be scoped to a subset of your accessible networks\n", "license": { "name": "Dual (AGPL3.0, Commercial License Available)" }, "version": "1" }, "paths": { "/api/auth/check-email": { "post": { "tags": [ "auth", "internal" ], "operationId": "check_email", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckEmailRequest" } } }, "required": true }, "responses": { "200": { "description": "Email is available", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "409": { "description": "Email already in use", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/forgot-password": { "post": { "tags": [ "auth", "internal" ], "operationId": "forgot_password", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ForgotPasswordRequest" } } }, "required": true }, "responses": { "200": { "description": "Password reset email sent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } } } } }, "/api/auth/login": { "post": { "tags": [ "auth", "internal" ], "operationId": "login", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginRequest" } } }, "required": true }, "responses": { "200": { "description": "Login successful", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "401": { "description": "Invalid credentials", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Login forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/logout": { "post": { "tags": [ "auth", "internal" ], "operationId": "logout", "responses": { "200": { "description": "Logout successful", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } } } } }, "/api/auth/me": { "post": { "tags": [ "auth", "internal" ], "operationId": "get_current_user", "responses": { "200": { "description": "Current user", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/oidc/{slug}/unlink": { "post": { "tags": [ "auth", "internal" ], "operationId": "unlink_oidc_account", "parameters": [ { "name": "slug", "in": "path", "description": "OIDC provider slug", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "OIDC account unlinked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Blocked in demo mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Provider not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/onboarding-state": { "get": { "tags": [ "auth", "internal" ], "summary": "Get current onboarding state from session", "operationId": "onboarding_state", "responses": { "200": { "description": "Onboarding state", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_OnboardingStateResponse" } } } } } } }, "/api/auth/onboarding-step": { "post": { "tags": [ "auth", "internal" ], "summary": "Store onboarding step in session", "operationId": "onboarding_step", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OnboardingStepRequest" } } }, "required": true }, "responses": { "200": { "description": "Step saved", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } } } } }, "/api/auth/register": { "post": { "tags": [ "auth", "internal" ], "operationId": "register", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterRequest" } } }, "required": true }, "responses": { "200": { "description": "User registered successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Registration disabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "Email already exists", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/request-email-change": { "post": { "tags": [ "auth", "internal" ], "operationId": "request_email_change", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestEmailChangeRequest" } } }, "required": true }, "responses": { "200": { "description": "Verification email sent to new address", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/resend-verification": { "post": { "tags": [ "auth", "internal" ], "operationId": "resend_verification", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResendVerificationRequest" } } }, "required": true }, "responses": { "200": { "description": "Verification email sent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request or already verified", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "429": { "description": "Rate limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/reset-password": { "post": { "tags": [ "auth", "internal" ], "operationId": "reset_password", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResetPasswordRequest" } } }, "required": true }, "responses": { "200": { "description": "Password reset successful", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "400": { "description": "Invalid or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/setup": { "post": { "tags": [ "auth", "internal" ], "summary": "Store pre-registration setup data (org name, networks, seed preference) in session", "operationId": "setup", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetupRequest" } } }, "required": true }, "responses": { "200": { "description": "Setup data stored", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_SetupResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/update": { "post": { "tags": [ "auth", "internal" ], "operationId": "update_password_auth", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdatePasswordRequest" } } }, "required": true }, "responses": { "200": { "description": "Password updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Blocked in demo mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/auth/verify-email": { "post": { "tags": [ "auth", "internal" ], "operationId": "verify_email", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerifyEmailRequest" } } }, "required": true }, "responses": { "200": { "description": "Email verified successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "400": { "description": "Invalid or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/billing/change-plan": { "post": { "tags": [ "billing", "internal" ], "summary": "Change billing plan", "description": "Upgrades or downgrades the organization's billing plan.", "operationId": "change_plan", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChangePlanRequest" } } }, "required": true }, "responses": { "200": { "description": "Plan change initiated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_String" } } } }, "400": { "description": "Invalid plan or billing not enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/billing/change-plan/preview": { "get": { "tags": [ "billing", "internal" ], "summary": "Preview plan change (shows overage counts)", "operationId": "preview_plan_change", "parameters": [ { "name": "plan", "in": "query", "description": "Target plan (JSON)", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Plan change preview", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_ChangePlanPreview" } } } }, "400": { "description": "Billing not enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/billing/checkout": { "post": { "tags": [ "billing", "internal" ], "summary": "Create a checkout session", "operationId": "create_checkout_session", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCheckoutRequest" } } }, "required": true }, "responses": { "200": { "description": "Checkout session URL", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_String" } } } }, "400": { "description": "Invalid plan or billing not enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/billing/inquiry": { "post": { "tags": [ "billing", "internal" ], "summary": "Submit enterprise plan inquiry", "description": "Updates Brevo contact/company with inquiry data, creates a deal, and\ntracks an event for automation triggers. Requires authentication to\nlink the inquiry to an organization.", "operationId": "submit_enterprise_inquiry", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EnterpriseInquiryRequest" } } }, "required": true }, "responses": { "200": { "description": "Inquiry submitted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid request or Brevo not configured", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "401": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/billing/plans": { "get": { "tags": [ "billing", "internal" ], "summary": "Get available billing plans", "operationId": "get_billing_plans", "responses": { "200": { "description": "List of available billing plans", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Vec_BillingPlan" } } } }, "400": { "description": "Billing not enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/billing/portal": { "post": { "tags": [ "billing", "internal" ], "summary": "Create a billing portal session", "operationId": "create_portal_session", "requestBody": { "content": { "text/plain": { "schema": { "type": "string" } } }, "required": true }, "responses": { "200": { "description": "Portal session URL", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_String" } } } }, "400": { "description": "Billing not enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/billing/setup-payment-method": { "post": { "tags": [ "billing", "internal" ], "summary": "Setup payment method (collect card without charging)", "operationId": "setup_payment_method", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetupPaymentMethodRequest" } } }, "required": true }, "responses": { "200": { "description": "Setup session URL", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_String" } } } }, "400": { "description": "Billing not enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/billing/webhooks": { "post": { "tags": [ "billing", "internal" ], "summary": "Handle Stripe webhook", "description": "Internal endpoint for Stripe webhook callbacks.", "operationId": "handle_webhook", "responses": { "200": { "description": "Webhook processed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid signature or billing not enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/config": { "get": { "tags": [ "config", "internal" ], "summary": "Get public server configuration", "description": "Returns public configuration settings like OIDC providers, billing status, etc.", "operationId": "get_public_config", "responses": { "200": { "description": "Public server configuration", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_PublicConfigResponse" } } } } } } }, "/api/daemons/register": { "post": { "tags": [ "Daemons", "internal" ], "summary": "Register a new Daemon", "description": "Internal endpoint for daemon self-registration. Creates a host entry\nand sets up default discovery jobs for the daemon.", "operationId": "register_daemon", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DaemonRegistrationRequest" } } }, "required": true }, "responses": { "200": { "description": "Daemon registered successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_DaemonRegistrationResponse" } } } }, "403": { "description": "Daemon registration disabled in demo mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/daemons/{id}/heartbeat": { "post": { "tags": [ "Daemons", "internal", "deprecated" ], "summary": "Receive daemon heartbeat (DEPRECATED - for backwards compatibility with pre-v0.14.0 daemons)", "description": "Internal endpoint for legacy daemons to send periodic heartbeats.\nNew daemons (v0.14.0+) use the /request-work endpoint which includes heartbeat functionality.\nThis endpoint is kept for backwards compatibility and will be removed in a future version.", "operationId": "receive_heartbeat", "parameters": [ { "name": "id", "in": "path", "description": "Daemon ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DaemonHeartbeatPayload" } } }, "required": true }, "responses": { "200": { "description": "Heartbeat received", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Daemon not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/daemons/{id}/request-work": { "post": { "tags": [ "Daemons", "internal" ], "summary": "Request work from server", "description": "Internal endpoint for daemons to poll for pending discovery sessions.\nAlso updates heartbeat and returns any pending cancellation requests.\nReturns tuple of (next_session, should_cancel).", "operationId": "receive_work_request", "parameters": [ { "name": "id", "in": "path", "description": "Daemon ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DaemonStatus" } } }, "required": true }, "responses": { "200": { "description": "Work request processed - returns (Option, bool)" }, "404": { "description": "Daemon not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/daemons/{id}/startup": { "post": { "tags": [ "Daemons", "internal" ], "summary": "Daemon startup handshake", "description": "Internal endpoint for daemons to report their version on startup.\nUpdates the daemon's version and last_seen timestamp, returns server capabilities.", "operationId": "daemon_startup", "parameters": [ { "name": "id", "in": "path", "description": "Daemon ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DaemonStartupRequest" } } }, "required": true }, "responses": { "200": { "description": "Startup acknowledged", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_ServerCapabilities" } } } }, "404": { "description": "Daemon not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/daemons/{id}/update-capabilities": { "post": { "tags": [ "Daemons", "internal" ], "summary": "Update Daemon capabilities", "description": "Internal endpoint for daemons to report their current capabilities.", "operationId": "update_capabilities", "parameters": [ { "name": "id", "in": "path", "description": "Daemon ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DaemonCapabilities" } } }, "required": true }, "responses": { "200": { "description": "Capabilities updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Daemon not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/github-stars": { "get": { "tags": [ "github", "internal" ], "summary": "Get GitHub star count", "description": "Returns the current star count for the Scanopy GitHub repository.", "operationId": "get_stars", "responses": { "200": { "description": "GitHub star count", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_u32" } } } } } } }, "/api/v1/auth/daemon": { "get": { "tags": [ "Daemon API Keys" ], "summary": "List all Daemon API Keys", "operationId": "list_daemon_api_keys", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of Daemon API Keys", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/DaemonApiKey" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Daemon API Keys" ], "summary": "Create Daemon API Key", "operationId": "create_daemon_api_key", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DaemonApiKey" } } }, "required": true }, "responses": { "200": { "description": "Daemon API key created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_DaemonApiKeyResponse" } } } }, "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Insufficient permissions (member+ required)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/auth/daemon/bulk-delete": { "post": { "tags": [ "Daemon API Keys" ], "summary": "Bulk delete daemon_api_keys", "description": "Returns 409 Conflict if any key is currently assigned to a daemon.", "operationId": "bulk_delete_daemon_api_keys", "requestBody": { "description": "Array of daemon_api_key IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "daemon_api_keys deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "409": { "description": "One or more API keys are in use by daemons", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/auth/daemon/export/csv": { "get": { "tags": [ "Daemon API Keys" ], "summary": "Export Daemon API Keys to CSV", "description": "Export all Daemon API Keys matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_daemon_api_keys_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Daemon API Keys", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/auth/daemon/{id}": { "get": { "tags": [ "Daemon API Keys" ], "summary": "Get Daemon API Key by ID", "operationId": "get_daemon_api_key_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Daemon API Key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Daemon API Key found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_DaemonApiKey" } } } }, "404": { "description": "Daemon API Key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Daemon API Keys" ], "summary": "Update a Daemon API Key", "operationId": "update_daemon_api_key", "parameters": [ { "name": "id", "in": "path", "description": "Daemon API key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DaemonApiKey" } } }, "required": true }, "responses": { "200": { "description": "Daemon API key updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_DaemonApiKey" } } } }, "404": { "description": "Daemon API key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Daemon API Keys" ], "summary": "Delete daemon_api_key", "description": "Returns 409 Conflict if the key is currently assigned to a daemon.", "operationId": "delete_daemon_api_key", "parameters": [ { "name": "id", "in": "path", "description": "daemon_api_key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "daemon_api_key deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "daemon_api_key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "API key is in use by a daemon", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/auth/daemon/{id}/rotate": { "post": { "tags": [ "Daemon API Keys" ], "summary": "Rotate a Daemon API Key", "operationId": "rotate_key_handler", "parameters": [ { "name": "id", "in": "path", "description": "Daemon API key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Daemon API key rotated, returns new key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_String" } } } }, "404": { "description": "Daemon API key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/auth/keys": { "get": { "tags": [ "User API Keys" ], "summary": "Get all user API keys for the current user", "operationId": "get_all_user_api_keys", "parameters": [ { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of user API keys", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_UserApiKey" } } } }, "401": { "description": "Not authenticated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] }, "post": { "tags": [ "User API Keys" ], "summary": "Create a new user API key", "operationId": "create_user_api_key", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserApiKey" } } }, "required": true }, "responses": { "200": { "description": "API key created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_UserApiKeyResponse" } } } }, "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Invalid permissions or network access", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] } }, "/api/v1/auth/keys/bulk-delete": { "post": { "tags": [ "User API Keys" ], "summary": "Bulk delete user API keys", "operationId": "bulk_delete_user_api_keys", "requestBody": { "description": "Array of API key IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "API keys deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "session": [] } ] } }, "/api/v1/auth/keys/export/csv": { "get": { "tags": [ "User API Keys" ], "summary": "Export User API Keys to CSV", "description": "Export all User API Keys matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_user_api_keys_csv", "parameters": [ { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing User API Keys", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/auth/keys/{id}": { "get": { "tags": [ "User API Keys" ], "summary": "Get a user API key by ID", "operationId": "get_user_api_key_by_id", "parameters": [ { "name": "id", "in": "path", "description": "API key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "API key found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_UserApiKey" } } } }, "404": { "description": "API key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] }, "put": { "tags": [ "User API Keys" ], "summary": "Update a user API key", "operationId": "update_user_api_key", "parameters": [ { "name": "id", "in": "path", "description": "API key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserApiKey" } } }, "required": true }, "responses": { "200": { "description": "API key updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_UserApiKey" } } } }, "403": { "description": "Not authorized to update this key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "API key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] }, "delete": { "tags": [ "User API Keys" ], "summary": "Delete a user API key", "operationId": "delete_user_api_key", "parameters": [ { "name": "id", "in": "path", "description": "API key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "API key deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "API key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] } }, "/api/v1/auth/keys/{id}/rotate": { "post": { "tags": [ "User API Keys" ], "summary": "Rotate a user API key", "operationId": "rotate_user_api_key", "parameters": [ { "name": "id", "in": "path", "description": "API key ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "API key rotated, returns new key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_String" } } } }, "403": { "description": "Not authorized to rotate this key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "API key not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] } }, "/api/v1/bindings": { "get": { "tags": [ "Bindings" ], "summary": "List all Bindings", "operationId": "list_bindings", "parameters": [ { "name": "service_id", "in": "query", "description": "Filter by service ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "port_id", "in": "query", "description": "Filter by port ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ip_address_id", "in": "query", "description": "Filter by interface ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of Bindings", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Binding" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Bindings" ], "summary": "Create a new Binding", "description": "Creates a binding that associates a service with a port or interface.\n\n### Binding Types\n\n- **Interface binding**: Service is present at an interface (IP address) without a specific port.\n Used for non-port-bound services like gateways.\n- **Port binding (specific ip_address)**: Service listens on a specific port on a specific interface.\n- **Port binding (all ip_addresses)**: Service listens on a specific port on all ip_addresses\n (`ip_address_id: null`).\n\n### Validation and Deduplication Rules\n\n- **Conflict detection**: Interface bindings conflict with port bindings on the same interface.\n A port binding on all ip_addresses conflicts with any interface binding for the same service.\n- **All-interfaces precedence**: When creating a port binding with `ip_address_id: null`,\n any existing specific-interface bindings for the same port are automatically removed,\n as they are superseded by the all-interfaces binding.", "operationId": "create_binding", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Binding" } } }, "required": true }, "responses": { "200": { "description": "Binding created (superseded bindings may be removed)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Binding" } } } }, "400": { "description": "Referenced port or ip_address does not exist", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "Conflict with existing binding type", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/bindings/bulk-delete": { "post": { "tags": [ "Bindings" ], "summary": "Bulk delete Bindings", "operationId": "bulk_delete_bindings", "requestBody": { "description": "Array of Binding IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Bindings deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/bindings/export/csv": { "get": { "tags": [ "Bindings" ], "summary": "Export Bindings to CSV", "description": "Export all Bindings matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_bindings_csv", "parameters": [ { "name": "service_id", "in": "query", "description": "Filter by service ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "port_id", "in": "query", "description": "Filter by port ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ip_address_id", "in": "query", "description": "Filter by interface ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Bindings", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/bindings/{id}": { "get": { "tags": [ "Bindings" ], "summary": "Get Binding by ID", "operationId": "get_binding_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Binding ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Binding found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Binding" } } } }, "404": { "description": "Binding not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Bindings" ], "summary": "Update a Binding", "description": "Updates an existing binding. The same conflict detection rules from binding creation apply.\n\n## Validation Rules\n\n- **Conflict detection**: The updated binding must not conflict with other bindings on the\n same service. Interface bindings conflict with port bindings on the same interface.", "operationId": "update_binding", "parameters": [ { "name": "id", "in": "path", "description": "Binding ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Binding" } } }, "required": true }, "responses": { "200": { "description": "Binding updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Binding" } } } }, "400": { "description": "Referenced port or ip_address does not exist", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "Conflict with existing binding type", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Bindings" ], "summary": "Delete Binding", "operationId": "delete_binding", "parameters": [ { "name": "id", "in": "path", "description": "Binding ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Binding deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Binding not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/credentials": { "get": { "tags": [ "Credentials" ], "summary": "List all Credentials", "description": "Returns all credentials in the authenticated user's organization.\nOptionally filter by type (e.g. ?type=Snmp).", "operationId": "get_all_credentials", "parameters": [ { "name": "type", "in": "query", "description": "Filter by credential type (e.g. \"Snmp\", \"DockerProxy\")", "required": false, "schema": { "type": [ "string", "null" ] } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CredentialOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CredentialOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of credentials", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_Credential" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Credentials" ], "summary": "Create a new Credential", "description": "Creates a credential scoped to your organization.", "operationId": "create_credential", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Credential" } } }, "required": true }, "responses": { "200": { "description": "Credential created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Credential" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/credentials/bulk": { "post": { "tags": [ "Credentials" ], "summary": "Bulk create Credentials", "description": "Creates multiple credentials in one request. Validation is atomic — if any\ncredential has an invalid type, none are created. Individual creates are\nsequential, so a mid-batch DB error leaves earlier credentials committed.", "operationId": "bulk_create_credentials", "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Credential" } } } }, "required": true }, "responses": { "200": { "description": "Credentials created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Vec_Credential" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/credentials/bulk-delete": { "post": { "tags": [ "Credentials" ], "summary": "Bulk delete Credentials", "operationId": "bulk_delete_credentials", "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Credentials deleted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/credentials/export/csv": { "get": { "tags": [ "Credentials" ], "summary": "Export Credentials to CSV", "description": "Export all Credentials matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_credentials_csv", "parameters": [ { "name": "type", "in": "query", "description": "Filter by credential type (e.g. \"Snmp\", \"DockerProxy\")", "required": false, "schema": { "type": [ "string", "null" ] } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CredentialOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CredentialOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Credentials", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/credentials/{id}": { "get": { "tags": [ "Credentials" ], "summary": "Get Credential by ID", "operationId": "get_credential_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Credential ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Credential found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Credential" } } } }, "404": { "description": "Credential not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Credentials" ], "summary": "Update Credential", "operationId": "update_credential", "parameters": [ { "name": "id", "in": "path", "description": "Credential ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Credential" } } }, "required": true }, "responses": { "200": { "description": "Credential updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Credential" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Credential not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Credentials" ], "summary": "Delete Credential", "operationId": "delete_credential", "parameters": [ { "name": "id", "in": "path", "description": "Credential ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Credential deleted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Credential not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons": { "get": { "tags": [ "Daemons" ], "summary": "Get all daemons", "description": "Returns all daemons accessible to the user.\nSupports pagination via `limit` and `offset` query parameters,\nand ordering via `group_by`, `order_by`, and `order_direction`.", "operationId": "get_daemons", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DaemonOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DaemonOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of daemons", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_DaemonResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons/bulk-delete": { "post": { "tags": [ "daemons" ], "summary": "Bulk delete daemons", "operationId": "bulk_delete_daemons", "requestBody": { "description": "Array of daemon IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "daemons deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "409": { "description": "daemon has active sessions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons/email-install-command": { "post": { "tags": [ "daemons" ], "summary": "Email install command to current user", "operationId": "email_install_command", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailInstallCommandRequest" } } }, "required": true }, "responses": { "200": { "description": "Email sent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Email service not configured", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons/export/csv": { "get": { "tags": [ "Daemons" ], "summary": "Export Daemons to CSV", "description": "Export all Daemons matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_daemons_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DaemonOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DaemonOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Daemons", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons/provision": { "post": { "tags": [ "internal", "Daemons" ], "summary": "Pre-provision a ServerPoll mode daemon", "description": "Creates a daemon record on the server before the daemon is installed.\nThis is only for ServerPoll mode where the server initiates connections to the daemon.\nFor DaemonPoll mode, daemons self-register on startup.\n\nReturns the daemon record and an API key that must be configured on the daemon.", "operationId": "provision_daemon", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProvisionDaemonRequest" } } }, "required": true }, "responses": { "201": { "description": "Daemon provisioned successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_ProvisionDaemonResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons/test-reachability": { "post": { "tags": [ "Daemons" ], "summary": "Test reachability of a daemon URL", "description": "Performs a TCP connection test and optionally an HTTP health check\nto verify that a daemon URL is reachable from the server.", "operationId": "test_daemon_reachability", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TestReachabilityRequest" } } }, "required": true }, "responses": { "200": { "description": "Reachability test result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_TestReachabilityResponse" } } } }, "400": { "description": "Invalid URL", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons/{id}": { "get": { "tags": [ "Daemons" ], "summary": "Get daemon by ID", "description": "Returns a specific daemon with computed version status.", "operationId": "get_daemon_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Daemon ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Daemon found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_DaemonResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Daemon not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "daemons" ], "summary": "Delete daemon", "operationId": "delete_daemon", "parameters": [ { "name": "id", "in": "path", "description": "daemon ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "daemon deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "daemon not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "daemon has active sessions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/daemons/{id}/retry-connection": { "post": { "tags": [ "Daemons" ], "summary": "Retry connection to unreachable daemon", "description": "Resets the is_unreachable flag for a daemon that was marked unreachable\ndue to repeated polling failures. The poller will attempt to contact\nthe daemon again on the next cycle.", "operationId": "retry_daemon_connection", "parameters": [ { "name": "id", "in": "path", "description": "Daemon ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Connection retry initiated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Daemon not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/dashboard/summary": { "get": { "tags": [ "dashboard", "internal" ], "summary": "Get dashboard summary", "description": "Returns aggregated dashboard data including network metrics, daemon health,\nrecent discoveries, and plan usage.", "operationId": "get_dashboard_summary", "responses": { "200": { "description": "Dashboard summary", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_DashboardSummary" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/dependencies": { "get": { "tags": [ "Dependencies" ], "summary": "List all Dependencies", "description": "Returns all dependencies the authenticated user has access to.\nSupports pagination via `limit` and `offset` query parameters,\nand ordering via `group_by`, `order_by`, and `order_direction`.", "operationId": "get_all_dependencies", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DependencyOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DependencyOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of dependencies", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_Dependency" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Dependencies" ], "summary": "Create a new Dependency", "operationId": "create_dependency", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Dependency" } } }, "required": true }, "responses": { "200": { "description": "Dependency created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Dependency" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/dependencies/bulk-delete": { "post": { "tags": [ "Dependencies" ], "summary": "Bulk delete Dependencies", "operationId": "bulk_delete_dependencies", "requestBody": { "description": "Array of Dependency IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Dependencies deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/dependencies/export/csv": { "get": { "tags": [ "Dependencies" ], "summary": "Export Dependencies to CSV", "description": "Export all Dependencies matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_dependencies_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DependencyOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/DependencyOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Dependencies", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/dependencies/{id}": { "get": { "tags": [ "Dependencies" ], "summary": "Get Dependency by ID", "operationId": "get_dependency_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Dependency ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Dependency found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Dependency" } } } }, "404": { "description": "Dependency not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Dependencies" ], "summary": "Update a Dependency", "operationId": "update_dependency", "parameters": [ { "name": "id", "in": "path", "description": "Dependency ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Dependency" } } }, "required": true }, "responses": { "200": { "description": "Dependency updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Dependency" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Dependency not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Dependencies" ], "summary": "Delete Dependency", "operationId": "delete_dependency", "parameters": [ { "name": "id", "in": "path", "description": "Dependency ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Dependency deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Dependency not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery": { "get": { "tags": [ "Discoveries" ], "summary": "List all Discoveries", "operationId": "list_discoveries", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "daemon_id", "in": "query", "description": "Filter by daemon ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of Discoveries", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Discovery" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Discoveries" ], "summary": "Create new Discovery", "operationId": "create_discovery", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Discovery" } } }, "required": true }, "responses": { "200": { "description": "Discovery created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Discovery" } } } }, "400": { "description": "Can't create historical discovery", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery/active-sessions": { "get": { "tags": [ "Discoveries" ], "summary": "Get active Discovery Sessions", "operationId": "get_active_sessions", "responses": { "200": { "description": "List of active discovery sessions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Vec_DiscoveryUpdatePayload" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery/bulk-delete": { "post": { "tags": [ "Discoveries" ], "summary": "Bulk delete discoveries", "operationId": "bulk_delete_discoveries", "requestBody": { "description": "Array of discovery IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "discoveries deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "409": { "description": "discovery has active session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery/export/csv": { "get": { "tags": [ "Discoveries" ], "summary": "Export Discoveries to CSV", "description": "Export all Discoveries matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_discoveries_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "daemon_id", "in": "query", "description": "Filter by daemon ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Discoveries", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery/start-session": { "post": { "tags": [ "Discoveries" ], "summary": "Start a Discovery Session", "operationId": "start_session", "requestBody": { "content": { "text/plain": { "schema": { "type": "string", "format": "uuid" } } }, "required": true }, "responses": { "200": { "description": "Discovery session started", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_DiscoveryUpdatePayload" } } } }, "404": { "description": "Discovery not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "A session is already running for this discovery", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery/{id}": { "get": { "tags": [ "Discoveries" ], "summary": "Get Discovery by ID", "operationId": "get_discovery_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Discovery ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Discovery found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Discovery" } } } }, "404": { "description": "Discovery not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Discoveries" ], "summary": "Update Discovery", "operationId": "update_discovery", "parameters": [ { "name": "id", "in": "path", "description": "Discovery ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Discovery" } } }, "required": true }, "responses": { "200": { "description": "Discovery updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Discovery" } } } }, "400": { "description": "Can't update historical discovery", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Discoveries" ], "summary": "Delete discovery", "operationId": "delete_discovery", "parameters": [ { "name": "id", "in": "path", "description": "discovery ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "discovery deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "discovery not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "discovery has active session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery/{session_id}/cancel": { "post": { "tags": [ "Discoveries" ], "summary": "Cancel a Discovery Session", "operationId": "cancel_discovery", "parameters": [ { "name": "session_id", "in": "path", "description": "Session ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Discovery session cancelled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/discovery/{session_id}/update": { "post": { "tags": [ "Discoveries", "internal" ], "summary": "Receive discovery progress update from daemon", "description": "Internal endpoint for daemons to report discovery progress.", "operationId": "receive_discovery_update", "parameters": [ { "name": "session_id", "in": "path", "description": "Discovery session ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscoveryUpdatePayload" } } }, "required": true }, "responses": { "200": { "description": "Update received", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/v1/hosts": { "get": { "tags": [ "Hosts" ], "summary": "List all hosts", "description": "Returns all hosts the authenticated user has access to, with their\nip_addresses, ports, and services included. Supports pagination via\n`limit` and `offset` query parameters, and ordering via `group_by`,\n`order_by`, and `order_direction`.", "operationId": "get_all_hosts", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "tag_ids", "in": "query", "description": "Filter by tag IDs (returns hosts that have ANY of the specified tags)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of hosts with their children", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_HostResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Hosts" ], "summary": "Create a new host", "description": "Creates a host with optional ip_addresses, ports, and services.\nThe `source` field is automatically set to `Manual`.\n\n### Tag Validation\n\n- Tags must exist and belong to your organization\n- Duplicate tag UUIDs are automatically deduplicated\n- Invalid or cross-organization tag UUIDs return a 400 error\n\n", "operationId": "create_host", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateHostRequest" } } }, "required": true }, "responses": { "200": { "description": "Host created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_HostResponse" } } } }, "400": { "description": "Validation error: network not found, subnet mismatch, or invalid tags", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "401": { "description": "No access to the specified network", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] }, { "daemon_api_key": [] } ] } }, "/api/v1/hosts/bulk-delete": { "post": { "tags": [ "Hosts" ], "summary": "Bulk delete hosts", "description": "Deletes multiple hosts in a single request. The request body should be\nan array of host IDs to delete. Fails if any host has an associated daemon.", "operationId": "bulk_delete_hosts", "requestBody": { "description": "Array of host IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Hosts deleted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "409": { "description": "One or more hosts has an associated daemon - delete daemons first", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/hosts/discovery": { "post": { "tags": [ "hosts", "internal" ], "summary": "Internal endpoint for daemon discovery", "description": "Used by daemons to report discovered hosts. Accepts full entities with\npre-generated IDs. Uses upsert behavior to merge with existing hosts.\n\nTagged as \"internal\" - included in OpenAPI spec for client generation\nbut hidden from public documentation.", "operationId": "create_host_discovery", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscoveryHostRequest" } } }, "required": true }, "responses": { "200": { "description": "Host discovered/updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_HostResponse" } } } }, "403": { "description": "Daemon cannot create hosts on other networks", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/v1/hosts/export/csv": { "get": { "tags": [ "Hosts" ], "summary": "Export Hosts to CSV", "description": "Export all Hosts matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_hosts_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "tag_ids", "in": "query", "description": "Filter by tag IDs (returns hosts that have ANY of the specified tags)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Hosts", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/hosts/export/zip": { "get": { "tags": [ "Hosts" ], "summary": "Export hosts with children to ZIP", "description": "Exports all hosts matching the filter criteria along with their children\n(ip_addresses, ports, services, interfaces) as a ZIP archive containing\nseparate CSV files for each entity type.", "operationId": "export_hosts_zip", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "tag_ids", "in": "query", "description": "Filter by tag IDs (returns hosts that have ANY of the specified tags)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "ZIP file containing CSVs", "content": { "application/zip": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/hosts/{destination_host}/consolidate/{other_host}": { "put": { "tags": [ "Hosts" ], "summary": "Consolidate hosts", "description": "Merges all ip_addresses, ports, and services from `other_host` into\n`destination_host`, then deletes `other_host`. Both hosts must be\non the same network.\n\n### Merge Behavior\n\n- **Interfaces**: Transferred to destination. If an interface with matching subnet+IP or MAC\n already exists on destination, bindings are remapped to use the existing interface.\n- **Ports**: Transferred to destination. If a port with the same number and protocol already\n exists, bindings are remapped to use the existing port.\n- **Services**: Transferred to destination with deduplication.\n See [upsert behavior](https://scanopy.net/docs/discovery/#upsert-behavior) for details.\n\n### Restrictions\n\n- Cannot consolidate a host with itself.\n- Cannot consolidate a host that has a daemon - consolidate into it instead.", "operationId": "consolidate_hosts", "parameters": [ { "name": "destination_host", "in": "path", "description": "Destination host ID - will receive all children", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "other_host", "in": "path", "description": "Host to merge into destination - will be deleted", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Hosts consolidated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_HostResponse" } } } }, "400": { "description": "Validation error: same host, has daemon, or different networks", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "One or both hosts not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/hosts/{id}": { "get": { "tags": [ "Hosts" ], "summary": "Get a host by ID", "description": "Returns a single host with its ip_addresses, ports, and services.", "operationId": "get_host_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Host ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Host found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_HostResponse" } } } }, "404": { "description": "Host not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Hosts" ], "summary": "Update a host", "description": "Updates host properties. Children (ip_addresses, ports, services)\nare managed via their own endpoints.\n\n### Tag Validation\n\n- Tags must exist and belong to your organization\n- Duplicate tag UUIDs are automatically deduplicated\n- Invalid or cross-organization tag UUIDs return a 400 error", "operationId": "update_host", "parameters": [ { "name": "id", "in": "path", "description": "Host ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateHostRequest" } } }, "required": true }, "responses": { "200": { "description": "Host updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_HostResponse" } } } }, "400": { "description": "Validation error: invalid tags", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Host not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Hosts" ], "summary": "Delete a host", "description": "Prevents deletion if the host has a daemon associated with it", "operationId": "delete_host", "parameters": [ { "name": "id", "in": "path", "description": "Host ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Host deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Host not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "Host has associated daemon", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/if-entries": { "get": { "tags": [ "Interfaces" ], "summary": "List all Interfaces", "operationId": "list_interfaces", "parameters": [ { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of Interfaces", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Interface" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Interfaces" ], "summary": "Create a new Interface", "description": "Creates an SNMP ifTable entry for a host. These are typically created by\nSNMP discovery, but can also be created manually.", "operationId": "create_if_entry", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Interface" } } }, "required": true }, "responses": { "200": { "description": "If entry created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Interface" } } } }, "400": { "description": "Network mismatch or duplicate if_index", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/if-entries/bulk-delete": { "post": { "tags": [ "Interfaces" ], "summary": "Bulk delete Interfaces", "operationId": "bulk_delete_interfaces", "requestBody": { "description": "Array of Interface IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Interfaces deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/if-entries/export/csv": { "get": { "tags": [ "Interfaces" ], "summary": "Export Interfaces to CSV", "description": "Export all Interfaces matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_interfaces_csv", "parameters": [ { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Interfaces", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/if-entries/{id}": { "get": { "tags": [ "Interfaces" ], "summary": "Get Interface by ID", "operationId": "get_interface_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Interface ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Interface found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Interface" } } } }, "404": { "description": "Interface not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Interfaces" ], "summary": "Update an Interface", "operationId": "update_if_entry", "parameters": [ { "name": "id", "in": "path", "description": "If entry ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Interface" } } }, "required": true }, "responses": { "200": { "description": "If entry updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Interface" } } } }, "400": { "description": "Network mismatch or invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "If entry not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Interfaces" ], "summary": "Delete Interface", "operationId": "delete_interface", "parameters": [ { "name": "id", "in": "path", "description": "Interface ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Interface deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Interface not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/invites": { "get": { "tags": [ "Invites" ], "summary": "List all invites", "operationId": "get_invites", "responses": { "200": { "description": "List of active invites", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Vec_Invite" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Invites" ], "summary": "Create invite", "operationId": "create_invite", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateInviteRequest" } } }, "required": true }, "responses": { "200": { "description": "Invite created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Invite" } } } }, "403": { "description": "Cannot create invite with higher permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/invites/{id}": { "get": { "tags": [ "Invites" ], "summary": "Get an invite by ID", "operationId": "get_invite", "parameters": [ { "name": "id", "in": "path", "description": "Invite ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Invite details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Invite" } } } }, "400": { "description": "Invalid or expired invite", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/invites/{id}/revoke": { "delete": { "tags": [ "Invites" ], "summary": "Revoke an invite", "operationId": "revoke_invite", "parameters": [ { "name": "id", "in": "path", "description": "Invite ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Invite revoked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid invite", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "403": { "description": "Cannot revoke this invite", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ip-addresses": { "get": { "tags": [ "IP Addresses" ], "summary": "List all IP Addresses", "operationId": "list_ip_addresses", "parameters": [ { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "subnet_id", "in": "query", "description": "Filter by subnet ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of IP Addresses", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/IPAddress" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "IP Addresses" ], "summary": "Create a new IP address\nPosition is automatically assigned to the end of the host's IP address list.", "operationId": "create_ip_address", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IPAddress" } } }, "required": true }, "responses": { "200": { "description": "IP address created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_IPAddress" } } } }, "400": { "description": "Network mismatch or invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ip-addresses/bulk-delete": { "post": { "tags": [ "IP Addresses" ], "summary": "Bulk delete IP addresses\nRemaining IP addresses for affected hosts are renumbered to maintain sequential positions.", "operationId": "bulk_delete_ip_addresses", "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "IP addresses deleted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "400": { "description": "No IDs provided", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ip-addresses/export/csv": { "get": { "tags": [ "IP Addresses" ], "summary": "Export IP Addresses to CSV", "description": "Export all IP Addresses matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_ip_addresses_csv", "parameters": [ { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "subnet_id", "in": "query", "description": "Filter by subnet ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing IP Addresses", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ip-addresses/{id}": { "get": { "tags": [ "IP Addresses" ], "summary": "Get IP Address by ID", "operationId": "get_ip_address_by_id", "parameters": [ { "name": "id", "in": "path", "description": "IP Address ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "IP Address found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_IPAddress" } } } }, "404": { "description": "IP Address not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "IP Addresses" ], "summary": "Update an IP address\nPosition must be within valid range and not conflict with other IP addresses.", "operationId": "update_ip_address", "parameters": [ { "name": "id", "in": "path", "description": "IP address ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IPAddress" } } }, "required": true }, "responses": { "200": { "description": "IP address updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_IPAddress" } } } }, "400": { "description": "Network mismatch or invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "IP address not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "IP Addresses" ], "summary": "Delete an IP address\nRemaining IP addresses for the host are renumbered to maintain sequential positions.", "operationId": "delete_ip_address", "parameters": [ { "name": "id", "in": "path", "description": "IP address ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "IP address deleted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "IP address not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/networks": { "get": { "tags": [ "Networks" ], "summary": "List all networks", "operationId": "get_all_networks", "parameters": [ { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of networks", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/NetworkBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "created_at": "2026-01-15T10:30:00Z", "credential_ids": [], "id": "550e8400-e29b-41d4-a716-446655440002", "name": "Home Network", "organization_id": "550e8400-e29b-41d4-a716-446655440001", "tags": [], "updated_at": "2026-01-15T10:30:00Z" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Networks" ], "summary": "Create a new network", "operationId": "create_network", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Network" } } }, "required": true }, "responses": { "200": { "description": "Network created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Network" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/networks/bulk-delete": { "post": { "tags": [ "Networks" ], "summary": "Bulk delete networks", "operationId": "bulk_delete_networks", "requestBody": { "description": "Array of network IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Networks deleted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "403": { "description": "User not admin", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/networks/export/csv": { "get": { "tags": [ "Networks" ], "summary": "Export Networks to CSV", "description": "Export all Networks matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_networks_csv", "parameters": [ { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Networks", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/networks/{id}": { "get": { "tags": [ "Networks" ], "summary": "Get a network by ID", "operationId": "get_by_id_network", "parameters": [ { "name": "id", "in": "path", "description": "Network ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Network found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Network" } } } }, "404": { "description": "Network not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Networks" ], "summary": "Update a network", "operationId": "update_network", "parameters": [ { "name": "id", "in": "path", "description": "Network ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Network" } } }, "required": true }, "responses": { "200": { "description": "Network updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Network" } } } }, "403": { "description": "User not admin", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Network not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Networks" ], "summary": "Delete a network", "operationId": "delete_network", "parameters": [ { "name": "id", "in": "path", "description": "Network ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Network deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "User not admin", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Network not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/organizations": { "get": { "tags": [ "Organizations" ], "summary": "Get the current user's organization", "operationId": "get_organization", "responses": { "200": { "description": "Organization details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Organization" } } } }, "404": { "description": "Organization not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] } }, "/api/v1/organizations/profile": { "post": { "tags": [ "Organizations" ], "summary": "Update user profile with deferred marketing fields", "operationId": "update_profile", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProfileUpdateRequest" } } }, "required": true }, "responses": { "200": { "description": "Profile updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } } } } }, "/api/v1/organizations/referral-source": { "post": { "tags": [ "Organizations" ], "summary": "Submit referral source (how did you hear about us)", "operationId": "submit_referral_source", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReferralSourceRequest" } } }, "required": true }, "responses": { "200": { "description": "Referral source recorded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } } } } }, "/api/v1/organizations/{id}": { "put": { "tags": [ "Organizations" ], "summary": "Update organization name", "operationId": "update_org_name", "parameters": [ { "name": "id", "in": "path", "description": "Organization ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "text/plain": { "schema": { "type": "string" } } }, "required": true }, "responses": { "200": { "description": "Organization updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Organization" } } } }, "403": { "description": "Only owners can update organization", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Organization not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Organizations", "internal" ], "summary": "Delete the organization entirely, including all data and users", "operationId": "delete_organization", "parameters": [ { "name": "id", "in": "path", "description": "Organization ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Cannot delete another organization", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Organization not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] } }, "/api/v1/organizations/{id}/populate-demo": { "post": { "tags": [ "Organizations", "internal" ], "summary": "Populate demo data (only available for demo organizations)", "operationId": "populate_demo_data", "parameters": [ { "name": "id", "in": "path", "description": "Organization ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Demo data populated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Only available for demo organizations", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Organization not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/organizations/{id}/reset": { "post": { "tags": [ "Organizations", "internal" ], "summary": "Reset all organization data (delete all entities except organization and owner user)", "operationId": "reset", "parameters": [ { "name": "id", "in": "path", "description": "Organization ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization reset", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Cannot reset another organization", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Organization not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ports": { "get": { "tags": [ "Ports" ], "summary": "List all Ports", "operationId": "list_ports", "parameters": [ { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of Ports", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Port" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Ports" ], "summary": "Create a new port", "operationId": "create_port", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Port" } } }, "required": true }, "responses": { "200": { "description": "Port created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Port" } } } }, "400": { "description": "Network mismatch or duplicate port", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ports/bulk-delete": { "post": { "tags": [ "Ports" ], "summary": "Bulk delete Ports", "operationId": "bulk_delete_ports", "requestBody": { "description": "Array of Port IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Ports deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ports/export/csv": { "get": { "tags": [ "Ports" ], "summary": "Export Ports to CSV", "description": "Export all Ports matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_ports_csv", "parameters": [ { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Ports", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/ports/{id}": { "get": { "tags": [ "Ports" ], "summary": "Get Port by ID", "operationId": "get_port_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Port ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Port found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Port" } } } }, "404": { "description": "Port not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Ports" ], "summary": "Update a port", "operationId": "update_port", "parameters": [ { "name": "id", "in": "path", "description": "Port ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Port" } } }, "required": true }, "responses": { "200": { "description": "Port updated successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Port" } } } }, "400": { "description": "Network mismatch or invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Port not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Ports" ], "summary": "Delete Port", "operationId": "delete_port", "parameters": [ { "name": "id", "in": "path", "description": "Port ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Port deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Port not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/services": { "get": { "tags": [ "Services" ], "summary": "List all services", "description": "Returns all services the authenticated user has access to.\nSupports pagination via `limit` and `offset` query parameters,\nand ordering via `group_by`, `order_by`, and `order_direction`.", "operationId": "get_all_services", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "tag_ids", "in": "query", "description": "Filter by tag IDs (returns services that have ANY of the specified tags)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServiceOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServiceOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "exclude_categories", "in": "query", "description": "Exclude services belonging to these categories.", "required": false, "schema": { "type": [ "array", "null" ], "items": { "$ref": "#/components/schemas/ServiceCategory" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of services", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_Service" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Services" ], "summary": "Create a new service", "description": "Creates a service with optional bindings to ip_addresses or ports.\nThe `id`, `created_at`, `updated_at`, and `source` fields are generated server-side.\nBindings are specified without `service_id` or `network_id` - these are assigned automatically.\n\n### Binding Validation Rules\n\n- **Cross-host validation**: All bindings must reference ports/interfaces that belong to the\n service's host. Bindings referencing entities from other hosts will be rejected.\n- **Deduplication**: Duplicate bindings in the same request are automatically deduplicated.\n- **All-interfaces precedence**: If a port binding with `ip_address_id: null` (all ip_addresses)\n is included, any specific-interface bindings for the same port are automatically removed.\n- **Conflict detection**: Interface bindings conflict with port bindings on the same interface.\n A port binding on all ip_addresses conflicts with any interface binding.", "operationId": "create_service", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateServiceRequest" } } }, "required": true }, "responses": { "200": { "description": "Service created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Service" } } } }, "400": { "description": "Validation error: host network mismatch, cross-host binding, or binding conflict", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/services/bulk-delete": { "post": { "tags": [ "Services" ], "summary": "Bulk delete Services", "operationId": "bulk_delete_services", "requestBody": { "description": "Array of Service IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Services deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/services/export/csv": { "get": { "tags": [ "Services" ], "summary": "Export Services to CSV", "description": "Export all Services matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_services_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "host_id", "in": "query", "description": "Filter by host ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "tag_ids", "in": "query", "description": "Filter by tag IDs (returns services that have ANY of the specified tags)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServiceOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServiceOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "exclude_categories", "in": "query", "description": "Exclude services belonging to these categories.", "required": false, "schema": { "type": [ "array", "null" ], "items": { "$ref": "#/components/schemas/ServiceCategory" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Services", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/services/{id}": { "get": { "tags": [ "Services" ], "summary": "Get Service by ID", "operationId": "get_service_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Service ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Service found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Service" } } } }, "404": { "description": "Service not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Services" ], "summary": "Update a service", "description": "Updates an existing service. All binding validation rules from service creation apply here as well.\n\n## Binding Validation Rules\n\n- **Cross-host validation**: All bindings must reference ports/interfaces that belong to the\n service's host. Bindings referencing entities from other hosts will be rejected.\n- **Deduplication**: Duplicate bindings are automatically deduplicated.\n- **All-interfaces precedence**: If a port binding with `ip_address_id: null` (all ip_addresses)\n is included, any specific-interface bindings for the same port are automatically removed.\n- **Conflict detection**: Interface bindings conflict with port bindings on the same interface.", "operationId": "update_service", "parameters": [ { "name": "id", "in": "path", "description": "Service ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Service" } } }, "required": true }, "responses": { "200": { "description": "Service updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Service" } } } }, "400": { "description": "Validation error: host network mismatch, cross-host binding, or binding conflict", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Service not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Services" ], "summary": "Delete Service", "operationId": "delete_service", "parameters": [ { "name": "id", "in": "path", "description": "Service ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Service deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Service not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/shares": { "get": { "tags": [ "Shares" ], "summary": "List all Shares", "operationId": "list_shares", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "topology_id", "in": "query", "description": "Filter by topology ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of Shares", "content": { "application/json": { "schema": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Share" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Shares" ], "summary": "Create a new share", "operationId": "create_share", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUpdateShareRequest" } } }, "required": true }, "responses": { "200": { "description": "Share created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Share" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/shares/bulk-delete": { "post": { "tags": [ "Shares" ], "summary": "Bulk delete Shares", "operationId": "bulk_delete_shares", "requestBody": { "description": "Array of Share IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Shares deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/shares/export/csv": { "get": { "tags": [ "Shares" ], "summary": "Export Shares to CSV", "description": "Export all Shares matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_shares_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "topology_id", "in": "query", "description": "Filter by topology ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Shares", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/shares/public/{id}": { "get": { "tags": [ "Shares" ], "summary": "Get share metadata", "description": "Does not include any topology data", "operationId": "get_public_share_metadata", "parameters": [ { "name": "id", "in": "path", "description": "Share ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Share metadata", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_PublicShareMetadata" } } } }, "404": { "description": "Share not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/v1/shares/public/{id}/verify": { "post": { "tags": [ "Shares", "internal" ], "summary": "Verify password for a password-protected share and return an access token.", "description": "The returned token is an HS256 JWT tied to the share's current password\nhash; subsequent `/topology` calls send the token instead of the raw\npassword. Changing the share password invalidates outstanding tokens.", "operationId": "verify_share_password", "parameters": [ { "name": "id", "in": "path", "description": "Share ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "text/plain": { "schema": { "type": "string" } } }, "required": true }, "responses": { "200": { "description": "Password verified; access token issued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_ShareAccessTokenResponse" } } } }, "401": { "description": "Invalid password", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Share not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } } } }, "/api/v1/shares/{id}": { "get": { "tags": [ "Shares" ], "summary": "Get Share by ID", "operationId": "get_share_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Share ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Share found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Share" } } } }, "404": { "description": "Share not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Shares" ], "summary": "Update a share", "operationId": "update_share", "parameters": [ { "name": "id", "in": "path", "description": "Share ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUpdateShareRequest" } } }, "required": true }, "responses": { "200": { "description": "Share updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Share" } } } }, "404": { "description": "Share not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Shares" ], "summary": "Delete Share", "operationId": "delete_share", "parameters": [ { "name": "id", "in": "path", "description": "Share ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Share deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Share not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/subnets": { "get": { "tags": [ "Subnets" ], "summary": "List all subnets", "description": "Returns all subnets accessible to the authenticated user or daemon.\nDaemons can only access subnets within their assigned network.\nSupports pagination via `limit` and `offset` query parameters,\nand ordering via `group_by`, `order_by`, and `order_direction`.", "operationId": "list_subnets", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/SubnetOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/SubnetOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of subnets", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_Subnet" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] }, { "daemon_api_key": [] } ] }, "post": { "tags": [ "Subnets" ], "summary": "Create a new subnet", "operationId": "create_subnet", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Subnet" } } }, "required": true }, "responses": { "200": { "description": "Subnet created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Subnet" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] }, { "daemon_api_key": [] } ] } }, "/api/v1/subnets/bulk-delete": { "post": { "tags": [ "Subnets" ], "summary": "Bulk delete Subnets", "operationId": "bulk_delete_subnets", "requestBody": { "description": "Array of Subnet IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Subnets deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/subnets/export/csv": { "get": { "tags": [ "Subnets" ], "summary": "Export Subnets to CSV", "description": "Export all Subnets matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_subnets_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/SubnetOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/SubnetOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Subnets", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/subnets/{id}": { "get": { "tags": [ "Subnets" ], "summary": "Get Subnet by ID", "operationId": "get_subnet_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Subnet ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Subnet found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Subnet" } } } }, "404": { "description": "Subnet not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Subnets" ], "summary": "Update a subnet", "description": "Updates subnet properties. If the CIDR is being changed, validates that\nall existing ip_addresses on this subnet have IPs within the new CIDR range.", "operationId": "update_subnet", "parameters": [ { "name": "id", "in": "path", "description": "Subnet ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Subnet" } } }, "required": true }, "responses": { "200": { "description": "Subnet updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Subnet" } } } }, "400": { "description": "CIDR change would orphan existing ip_addresses", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Subnet not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Subnets" ], "summary": "Delete Subnet", "operationId": "delete_subnet", "parameters": [ { "name": "id", "in": "path", "description": "Subnet ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Subnet deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Subnet not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/tags": { "get": { "tags": [ "Tags" ], "summary": "List all tags", "description": "Returns all tags in the authenticated user's organization.\nSupports pagination via `limit` and `offset` query parameters,\nand ordering via `group_by`, `order_by`, and `order_direction`.", "operationId": "get_all_tags", "parameters": [ { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TagOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TagOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of tags", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_Tag" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Tags" ], "summary": "Create a new tag", "description": "Creates a tag scoped to your organization. Tag names must be unique within the organization.\n\n### Validation\n\n- Name must be 1-100 characters (empty names are rejected)\n- Name must be unique within your organization", "operationId": "create_tag", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Tag" } } }, "required": true }, "responses": { "200": { "description": "Tag created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Tag" } } } }, "400": { "description": "Validation error: name empty or too long", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "Tag name already exists in this organization", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/tags/assign": { "put": { "tags": [ "Tags" ], "summary": "Set all tags for an entity", "description": "Replaces all tags on an entity with the provided list.\n\n### Validation\n\n- Entity type must be taggable (Host, Service, Subnet, Group, Network, Discovery, Daemon, DaemonApiKey, UserApiKey)\n- All tags must exist and belong to your organization", "operationId": "set_entity_tags", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetTagsRequest" } } }, "required": true }, "responses": { "200": { "description": "Tags set successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "400": { "description": "Invalid entity type or tag", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Tag not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/tags/assign/bulk-add": { "post": { "tags": [ "Tags" ], "summary": "Bulk add a tag to multiple entities", "description": "Adds a single tag to multiple entities of the same type. This is useful for batch tagging operations.\n\n### Validation\n\n- Entity type must be taggable (Host, Service, Subnet, Group, Network, Discovery, Daemon, DaemonApiKey, UserApiKey)\n- Tag must exist and belong to your organization\n- Entities that already have the tag are silently skipped", "operationId": "bulk_add_tag", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkTagRequest" } } }, "required": true }, "responses": { "200": { "description": "Tag added successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkTagResponse" } } } }, "400": { "description": "Invalid entity type or tag", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Tag not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/tags/assign/bulk-remove": { "post": { "tags": [ "Tags" ], "summary": "Bulk remove a tag from multiple entities", "description": "Removes a single tag from multiple entities of the same type.\n\n### Validation\n\n- Entity type must be taggable (Host, Service, Subnet, Group, Network, Discovery, Daemon, DaemonApiKey, UserApiKey)\n- Entities that don't have the tag are silently skipped", "operationId": "bulk_remove_tag", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkTagRequest" } } }, "required": true }, "responses": { "200": { "description": "Tag removed successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkTagResponse" } } } }, "400": { "description": "Invalid entity type", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/tags/bulk-delete": { "post": { "tags": [ "Tags" ], "summary": "Bulk delete Tags", "operationId": "bulk_delete_tags", "requestBody": { "description": "Array of Tag IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Tags deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/tags/export/csv": { "get": { "tags": [ "Tags" ], "summary": "Export Tags to CSV", "description": "Export all Tags matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_tags_csv", "parameters": [ { "name": "group_by", "in": "query", "description": "Primary ordering field (used for grouping). Always sorts ASC to keep groups together.", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TagOrderField" } ] } }, { "name": "order_by", "in": "query", "description": "Secondary ordering field (sorting within groups or standalone sort).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TagOrderField" } ] } }, { "name": "order_direction", "in": "query", "description": "Direction for order_by field (group_by always uses ASC).", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Tags", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/tags/{id}": { "get": { "tags": [ "Tags" ], "summary": "Get Tag by ID", "operationId": "get_tag_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Tag ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Tag found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Tag" } } } }, "404": { "description": "Tag not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Tags" ], "summary": "Update Tag", "operationId": "update_tag", "parameters": [ { "name": "id", "in": "path", "description": "Tag ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Tag" } } }, "required": true }, "responses": { "200": { "description": "Tag updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Tag" } } } }, "404": { "description": "Tag not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Tags" ], "summary": "Delete Tag", "operationId": "delete_tag", "parameters": [ { "name": "id", "in": "path", "description": "Tag ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Tag deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Tag not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology": { "get": { "tags": [ "Topologies", "internal" ], "summary": "Get all topologies", "operationId": "get_all_topologies", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of topologies", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_Topology" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Topologies", "internal" ], "summary": "Create topology", "operationId": "create_topology", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Topology" } } }, "required": true }, "responses": { "200": { "description": "Topology created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Topology" } } } }, "400": { "description": "Validation failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/export/csv": { "get": { "tags": [ "Topologies" ], "summary": "Export Topologies to CSV", "description": "Export all Topologies matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_topologies_csv", "parameters": [ { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } }, { "name": "ids", "in": "query", "description": "Filter by specific entity IDs (for selective loading)", "required": false, "schema": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } } }, { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Topologies", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}": { "get": { "tags": [ "Topologies" ], "summary": "Get Topology by ID", "operationId": "get_topology_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Topology found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Topology" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Topologies", "internal" ], "operationId": "update_topology", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Topology" } } }, "required": true }, "responses": { "200": { "description": "Topology updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Topology" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Topologies", "internal" ], "summary": "Delete a topology", "description": "Prevents deletion of the last topology on a network.", "operationId": "delete_topology", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Topology deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "Cannot delete last topology", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/edge-handles": { "post": { "tags": [ "Topologies", "internal" ], "summary": "Update an edge's handles", "description": "Lightweight endpoint for edge reconnect operations. Instead of sending the entire\ntopology, only sends the edge ID and new handle positions.\nFixes HTTP 413 errors on edge reconnect operations for large topologies.", "operationId": "update_edge_handles", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TopologyEdgeHandleUpdate" } } }, "required": true }, "responses": { "200": { "description": "Edge handles updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology or edge not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/export/confluence": { "get": { "tags": [ "Topologies", "internal" ], "summary": "Export topology as Confluence wiki markup", "operationId": "export_confluence", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Confluence wiki markup export", "content": { "text/plain": {} } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/export/mermaid": { "get": { "tags": [ "Topologies", "internal" ], "summary": "Export topology as Mermaid flowchart", "operationId": "export_mermaid", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Mermaid flowchart export", "content": { "text/plain": {} } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/lock": { "post": { "tags": [ "Topologies" ], "summary": "Lock a topology", "operationId": "lock", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Topology locked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Topology" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/metadata": { "post": { "tags": [ "Topologies", "internal" ], "summary": "Update topology metadata", "description": "Lightweight endpoint for editing topology name and parent. Instead of sending\nthe entire topology (which includes all hosts, ip_addresses, services, etc.),\nonly sends the metadata fields.\nFixes HTTP 413 errors on metadata edit operations for large topologies.", "operationId": "update_metadata", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TopologyMetadataUpdate" } } }, "required": true }, "responses": { "200": { "description": "Metadata updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/node-position": { "post": { "tags": [ "Topologies", "internal" ], "summary": "Update a single node's position", "description": "Lightweight endpoint for drag operations. Instead of sending the entire topology\n(which can be several megabytes), only sends the node ID and new position.\nFixes HTTP 413 errors on drag operations for large topologies.", "operationId": "update_node_position", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TopologyNodePositionUpdate" } } }, "required": true }, "responses": { "200": { "description": "Node position updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology or node not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/node-resize": { "post": { "tags": [ "Topologies", "internal" ], "summary": "Update a node's size and position", "description": "Lightweight endpoint for subnet resize operations. Instead of sending the entire\ntopology, only sends the node ID, new size, and new position.\nFixes HTTP 413 errors on resize operations for large topologies.", "operationId": "update_node_resize", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TopologyNodeResizeUpdate" } } }, "required": true }, "responses": { "200": { "description": "Node resized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology or node not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/rebuild": { "post": { "tags": [ "Topologies", "internal" ], "summary": "Rebuild topology layout", "operationId": "rebuild", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TopologyRebuildRequest" } } }, "required": true }, "responses": { "200": { "description": "Topology rebuilt", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/refresh": { "post": { "tags": [ "Topologies", "internal" ], "summary": "Refresh topology data", "operationId": "refresh", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TopologyRebuildRequest" } } }, "required": true }, "responses": { "200": { "description": "Topology refreshed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/topology/{id}/unlock": { "post": { "tags": [ "Topologies" ], "summary": "Unlock a topology", "operationId": "unlock", "parameters": [ { "name": "id", "in": "path", "description": "Topology ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Topology unlocked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Topology" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "Topology not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/users": { "get": { "tags": [ "Users" ], "summary": "List all users", "operationId": "get_all_users", "parameters": [ { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "List of users", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_User" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/users/bulk-delete": { "post": { "tags": [ "Users" ], "summary": "Bulk delete users", "operationId": "bulk_delete_users", "requestBody": { "description": "Array of user IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Users deleted successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } }, "403": { "description": "Cannot delete users with higher permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/users/export/csv": { "get": { "tags": [ "Users" ], "summary": "Export Users to CSV", "description": "Export all Users matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_users_csv", "parameters": [ { "name": "limit", "in": "query", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "description": "Number of results to skip. Default: 0.", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "CSV file containing Users", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/users/{id}": { "get": { "tags": [ "Users" ], "summary": "Get user by ID", "operationId": "get_user_by_id", "parameters": [ { "name": "id", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "403": { "description": "Access denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "User not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] }, "put": { "tags": [ "Users", "internal" ], "summary": "Update your own user record", "operationId": "update_user", "parameters": [ { "name": "id", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }, "required": true }, "responses": { "200": { "description": "User updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "403": { "description": "Cannot update another user's record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "User not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "session": [] } ] }, "delete": { "tags": [ "Users" ], "summary": "Delete a user", "operationId": "delete_user", "parameters": [ { "name": "id", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "403": { "description": "Cannot delete user with higher permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "User not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "Cannot delete the only owner", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/users/{id}/admin": { "put": { "tags": [ "Users", "internal" ], "summary": "Admin update user (for changing permissions)", "operationId": "admin_update_user", "parameters": [ { "name": "id", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }, "required": true }, "responses": { "200": { "description": "User updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_User" } } } }, "403": { "description": "Cannot update user with higher permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "404": { "description": "User not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/vlans": { "get": { "tags": [ "Vlans" ], "summary": "List all VLANs", "description": "Returns VLANs accessible to the authenticated user, optionally filtered by network.", "operationId": "get_all_vlans", "parameters": [ { "name": "group_by", "in": "query", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/VlanOrderField" } ] } }, { "name": "order_by", "in": "query", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/VlanOrderField" } ] } }, { "name": "order_direction", "in": "query", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } } ], "responses": { "200": { "description": "List of VLANs", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedApiResponse_Vlan" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "post": { "tags": [ "Vlans" ], "summary": "Create a new VLAN", "description": "Creates a VLAN scoped to a network. VLAN numbers must be unique within a network.", "operationId": "create_vlan", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Vlan" } } }, "required": true }, "responses": { "200": { "description": "VLAN created successfully", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Vlan" } } } }, "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } }, "409": { "description": "VLAN number already exists in this network", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/vlans/bulk-delete": { "post": { "tags": [ "Vlans" ], "summary": "Bulk delete Vlans", "operationId": "bulk_delete_vlans", "requestBody": { "description": "Array of Vlan IDs to delete", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Vlans deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_BulkDeleteResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/vlans/discovery": { "post": { "tags": [ "Vlans" ], "summary": "Bulk upsert VLANs from discovery", "description": "Used by daemons to report discovered VLANs. Creates new VLANs or updates names.\nReturns the mapping of VLAN numbers to entity UUIDs for Interface construction.", "operationId": "discovery_upsert_vlans", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VlanDiscoveryRequest" } } }, "required": true }, "responses": { "200": { "description": "VLANs upserted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_VlanDiscoveryResponse" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "daemon_api_key": [] } ] } }, "/api/v1/vlans/export/csv": { "get": { "tags": [ "Vlans" ], "summary": "Export Vlans to CSV", "description": "Export all Vlans matching the filter criteria to CSV format. Ignores pagination parameters (limit/offset) and exports all matching records.", "operationId": "export_vlans_csv", "parameters": [ { "name": "group_by", "in": "query", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/VlanOrderField" } ] } }, { "name": "order_by", "in": "query", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/VlanOrderField" } ] } }, { "name": "order_direction", "in": "query", "required": false, "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OrderDirection" } ] } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "maximum": 1000, "minimum": 0 } }, { "name": "offset", "in": "query", "required": false, "schema": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 } }, { "name": "network_id", "in": "query", "description": "Filter by network ID", "required": false, "schema": { "type": [ "string", "null" ], "format": "uuid" } } ], "responses": { "200": { "description": "CSV file containing Vlans", "content": { "text/csv": {} } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/v1/vlans/{id}": { "get": { "tags": [ "Vlans" ], "summary": "Get Vlan by ID", "operationId": "get_vlan_by_id", "parameters": [ { "name": "id", "in": "path", "description": "Vlan ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Vlan found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Vlan" } } } }, "404": { "description": "Vlan not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "put": { "tags": [ "Vlans" ], "summary": "Update Vlan", "operationId": "update_vlan", "parameters": [ { "name": "id", "in": "path", "description": "Vlan ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Vlan" } } }, "required": true }, "responses": { "200": { "description": "Vlan updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_Vlan" } } } }, "404": { "description": "Vlan not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] }, "delete": { "tags": [ "Vlans" ], "summary": "Delete Vlan", "operationId": "delete_vlan", "parameters": [ { "name": "id", "in": "path", "description": "Vlan ID", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Vlan deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } }, "404": { "description": "Vlan not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiErrorResponse" } } } } }, "security": [ { "user_api_key": [] }, { "session": [] } ] } }, "/api/version": { "get": { "tags": [ "system", "internal" ], "summary": "Get API version information", "operationId": "get_version", "responses": { "200": { "description": "Version information", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse_VersionInfo" } } } } } } } }, "components": { "schemas": { "ApiErrorResponse": { "type": "object", "description": "Error response type for API errors (no data field)", "required": [ "success", "meta" ], "properties": { "code": { "type": [ "string", "null" ], "description": "Machine-readable error code for i18n translation" }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta", "description": "API metadata (version info)" }, "params": { "type": [ "object", "null" ], "description": "Parameters for interpolating into the translated error message", "additionalProperties": {}, "propertyNames": { "type": "string" } }, "success": { "type": "boolean" } } }, "ApiMeta": { "type": "object", "description": "API metadata included in all responses", "required": [ "api_version", "server_version" ], "properties": { "api_version": { "type": "integer", "format": "int32", "description": "API version (integer, increments on breaking changes)", "minimum": 0 }, "server_version": { "type": "string", "description": "Server version (semver)", "example": "0.16.0" } }, "example": { "api_version": 1, "server_version": "0.16.0" } }, "ApiResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TupleUnit" } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Binding": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/BindingBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "description": "Association between a service and a port / interface that the service is listening on", "example": { "created_at": "2026-04-18T15:49:08.322494Z", "id": "3f6a903d-fab5-44af-b98e-7dba1aba9eb2", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.322494Z" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_BulkDeleteResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "required": [ "deleted_count", "requested_count" ], "properties": { "deleted_count": { "type": "integer", "minimum": 0 }, "requested_count": { "type": "integer", "minimum": 0 } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_BulkTagResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response for bulk tag operations", "required": [ "affected_count" ], "properties": { "affected_count": { "type": "integer", "description": "Number of entities affected", "minimum": 0 } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_ChangePlanPreview": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "required": [ "excess_hosts", "excess_networks", "excess_seats" ], "properties": { "excess_hosts": { "type": "integer", "format": "int64", "minimum": 0 }, "excess_networks": { "type": "integer", "format": "int64", "minimum": 0 }, "excess_seats": { "type": "integer", "format": "int64", "minimum": 0 } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Credential": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/CredentialBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_DaemonApiKey": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/DaemonApiKeyBase" }, { "type": "object", "required": [ "id", "updated_at", "created_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_DaemonApiKeyResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "required": [ "api_key", "key" ], "properties": { "api_key": { "$ref": "#/components/schemas/DaemonApiKey" }, "key": { "type": "string" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_DaemonRegistrationResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Daemon registration response from server to daemon", "required": [ "daemon", "host_id" ], "properties": { "daemon": { "$ref": "#/components/schemas/Daemon" }, "host_id": { "type": "string", "format": "uuid" }, "server_capabilities": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServerCapabilities", "description": "Server capabilities (returned if daemon sends version info)" } ] } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_DaemonResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/DaemonBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at", "version_status" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "version_status": { "$ref": "#/components/schemas/DaemonVersionStatus", "description": "Computed version status including health and warnings" } } } ], "description": "Daemon response for UI including computed version status" }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_DashboardSummary": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Dashboard summary response", "required": [ "networks", "daemons", "recent_discoveries", "plan_usage" ], "properties": { "daemons": { "type": "array", "items": { "$ref": "#/components/schemas/DaemonResponse" } }, "networks": { "type": "array", "items": { "$ref": "#/components/schemas/NetworkSummary" } }, "plan_usage": { "$ref": "#/components/schemas/PlanUsage" }, "recent_discoveries": { "type": "array", "items": { "$ref": "#/components/schemas/Discovery" } } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Dependency": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/DependencyBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "color": "Blue", "created_at": "2026-01-15T10:30:00Z", "dependency_type": "RequestPath", "description": "HTTP/HTTPS services dependency", "edge_style": "Bezier", "id": "550e8400-e29b-41d4-a716-446655440008", "members": { "service_ids": [], "type": "Services" }, "name": "Web Services", "network_id": "550e8400-e29b-41d4-a716-446655440002", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Discovery": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/DiscoveryBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "force_full_scan": { "type": "boolean", "description": "When true, the next scan will be a full port scan regardless of interval" }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "pending_credential_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Credential IDs to include in the next scan's credential_mappings.\nSet by the discovery edit modal, cleared after each scan completes." }, "scan_count": { "type": "integer", "format": "int32", "description": "Number of completed scans (incremented by server on session completion)", "readOnly": true, "minimum": 0 }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_DiscoveryUpdatePayload": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Progress update from daemon to server during discovery", "required": [ "session_id", "daemon_id", "network_id", "phase", "discovery_type", "progress" ], "properties": { "daemon_id": { "type": "string", "format": "uuid" }, "discovery_id": { "type": [ "string", "null" ], "format": "uuid", "description": "The discovery configuration this session belongs to.\nAlways enriched server-side; daemons do not send this field." }, "discovery_type": { "$ref": "#/components/schemas/DiscoveryType" }, "error": { "type": [ "string", "null" ] }, "estimated_remaining_secs": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 }, "finished_at": { "type": [ "string", "null" ], "format": "date-time" }, "hosts_discovered": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 }, "network_id": { "type": "string", "format": "uuid" }, "phase": { "$ref": "#/components/schemas/DiscoveryPhase" }, "progress": { "type": "integer", "format": "int32", "minimum": 0 }, "session_id": { "type": "string", "format": "uuid" }, "started_at": { "type": [ "string", "null" ], "format": "date-time" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_HostResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response type for host endpoints.\nIncludes children (ip_addresses, ports, services, interfaces).", "required": [ "id", "created_at", "updated_at", "name", "network_id", "source", "hidden", "tags", "ip_addresses", "ports", "services", "interfaces" ], "properties": { "chassis_id": { "type": [ "string", "null" ] }, "created_at": { "type": "string", "format": "date-time" }, "credential_assignments": { "type": "array", "items": { "$ref": "#/components/schemas/CredentialAssignment" } }, "description": { "type": [ "string", "null" ] }, "hidden": { "type": "boolean" }, "hostname": { "type": [ "string", "null" ] }, "id": { "type": "string", "format": "uuid" }, "interfaces": { "type": "array", "items": { "$ref": "#/components/schemas/Interface" }, "description": "SNMP ifTable entries" }, "ip_addresses": { "type": "array", "items": { "$ref": "#/components/schemas/IPAddress" } }, "management_url": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "ports": { "type": "array", "items": { "$ref": "#/components/schemas/Port" } }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/Service" } }, "source": { "$ref": "#/components/schemas/EntitySource" }, "sys_contact": { "type": [ "string", "null" ] }, "sys_descr": { "type": [ "string", "null" ] }, "sys_location": { "type": [ "string", "null" ] }, "sys_object_id": { "type": [ "string", "null" ] }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "updated_at": { "type": "string", "format": "date-time" }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostVirtualization" } ] } }, "example": { "created_at": "2026-01-15T10:30:00Z", "credential_assignments": [], "description": "Primary web server", "hidden": false, "hostname": "web-server-01.local", "id": "550e8400-e29b-41d4-a716-446655440003", "interfaces": [ { "admin_status": "Up", "cdp_address": null, "cdp_device_id": null, "cdp_platform": null, "cdp_port_id": null, "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-44665544000f", "if_alias": "Uplink to Core Switch", "if_descr": "GigabitEthernet0/1", "if_index": 1, "if_name": "Gi0/1", "if_type": 6, "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "lldp_chassis_id": null, "lldp_mgmt_addr": null, "lldp_port_desc": null, "lldp_port_id": null, "lldp_sys_desc": null, "lldp_sys_name": null, "mac_address": "DE:AD:BE:EF:CA:FE", "neighbor": null, "network_id": "550e8400-e29b-41d4-a716-446655440002", "oper_status": "Up", "speed_bps": 1000000000, "updated_at": "2026-01-15T10:30:00Z" } ], "ip_addresses": [ { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440005", "ip_address": "192.168.1.100", "mac_address": "DE:AD:BE:EF:CA:FE", "name": "eth0", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "subnet_id": "550e8400-e29b-41d4-a716-446655440004", "updated_at": "2026-01-15T10:30:00Z" } ], "name": "web-server-01", "network_id": "550e8400-e29b-41d4-a716-446655440002", "ports": [ { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440006", "network_id": "550e8400-e29b-41d4-a716-446655440002", "number": 80, "protocol": "Tcp", "type": "Http", "updated_at": "2026-01-15T10:30:00Z" } ], "services": [ { "bindings": [ { "created_at": "2026-04-18T15:49:08.301365Z", "id": "f152f7bb-59d1-4b07-8d1d-dd5cef57360b", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.301365Z" } ], "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440007", "name": "nginx", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "service_definition": "Zabbix Agent", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } ], "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_IPAddress": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/IPAddressBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440005", "ip_address": "192.168.1.100", "mac_address": "DE:AD:BE:EF:CA:FE", "name": "eth0", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "subnet_id": "550e8400-e29b-41d4-a716-446655440004", "updated_at": "2026-01-15T10:30:00Z" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Interface": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/InterfaceBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Invite": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/InviteBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Network": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/NetworkBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "created_at": "2026-01-15T10:30:00Z", "credential_ids": [], "id": "550e8400-e29b-41d4-a716-446655440002", "name": "Home Network", "organization_id": "550e8400-e29b-41d4-a716-446655440001", "tags": [], "updated_at": "2026-01-15T10:30:00Z" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_OnboardingStateResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response from onboarding state endpoint", "properties": { "network": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OnboardingNetworkState", "description": "Network from pending setup (with name and ID)" } ] }, "network_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Network ID from pending setup (if any)" }, "org_name": { "type": [ "string", "null" ], "description": "Organization name from pending setup" }, "step": { "type": [ "string", "null" ], "description": "Current onboarding step (if any)" }, "use_case": { "type": [ "string", "null" ], "description": "Use case selection (homelab, company, msp)" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Organization": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/OrganizationBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Port": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/PortBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "description": "Port entity with custom serialization that flattens PortType fields.", "example": { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440006", "network_id": "550e8400-e29b-41d4-a716-446655440002", "number": 80, "protocol": "Tcp", "type": "Http", "updated_at": "2026-01-15T10:30:00Z" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_ProvisionDaemonResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response from provisioning a daemon.\nContains the daemon record and the API key (shown only once).", "required": [ "daemon", "daemon_api_key" ], "properties": { "daemon": { "$ref": "#/components/schemas/DaemonResponse", "description": "The created daemon record (with version status)." }, "daemon_api_key": { "type": "string", "description": "The API key (plaintext) for daemon authentication.\nThis is shown only once - store it securely." } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_PublicConfigResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "required": [ "server_port", "disable_registration", "disable_password_login", "oidc_providers", "billing_enabled", "has_integrated_daemon", "has_email_service", "has_email_opt_in", "public_url", "needs_cookie_consent", "deployment_type" ], "properties": { "billing_enabled": { "type": "boolean" }, "deployment_type": { "$ref": "#/components/schemas/DeploymentType" }, "disable_password_login": { "type": "boolean" }, "disable_registration": { "type": "boolean" }, "has_email_opt_in": { "type": "boolean" }, "has_email_service": { "type": "boolean" }, "has_integrated_daemon": { "type": "boolean" }, "license_expiry": { "type": [ "string", "null" ] }, "license_status": { "type": [ "string", "null" ] }, "needs_cookie_consent": { "type": "boolean" }, "oidc_providers": { "type": "array", "items": { "$ref": "#/components/schemas/OidcProviderMetadata" } }, "posthog_key": { "type": [ "string", "null" ] }, "public_url": { "type": "string" }, "server_port": { "type": "integer", "format": "int32", "minimum": 0 } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_PublicShareMetadata": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Public share metadata (returned without authentication)", "required": [ "id", "name", "requires_password", "options", "enabled_views" ], "properties": { "enabled_views": { "type": "array", "items": { "$ref": "#/components/schemas/TopologyView" }, "description": "Resolved list of available topology views for this share.\nFiltered by both share configuration and data availability.\nFirst element is the default view." }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "options": { "$ref": "#/components/schemas/ShareOptions" }, "requires_password": { "type": "boolean" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_ServerCapabilities": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Server capabilities returned on startup/registration", "required": [ "server_version", "minimum_daemon_version" ], "properties": { "deprecation_warnings": { "type": "array", "items": { "$ref": "#/components/schemas/DeprecationWarning" }, "description": "Deprecation warnings for the daemon" }, "minimum_daemon_version": { "type": "string", "description": "Minimum daemon version supported by this server" }, "server_version": { "type": "string", "description": "Server software version" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Service": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/ServiceBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "bindings": [ { "created_at": "2026-04-18T15:49:08.316844Z", "id": "c04fc227-0dc8-49ac-92f5-956b727e3c11", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.316844Z" } ], "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440007", "name": "nginx", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "service_definition": "Zabbix Agent", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_SetupResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response from setup endpoint", "required": [ "network_id" ], "properties": { "network_id": { "type": "string", "format": "uuid" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Share": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/ShareBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_ShareAccessTokenResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Access token returned after successful password verification.\n\nThe token is an HS256 JWT tied to the share's `password_hash` — changing\nthe share password implicitly invalidates all outstanding tokens.", "required": [ "access_token", "expires_at" ], "properties": { "access_token": { "type": "string" }, "expires_at": { "type": "string", "format": "date-time" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_String": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "string" }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Subnet": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/SubnetBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "cidr": "192.168.1.0/24", "created_at": "2026-01-15T10:30:00Z", "description": "Local area network", "id": "550e8400-e29b-41d4-a716-446655440004", "name": "LAN", "network_id": "550e8400-e29b-41d4-a716-446655440002", "source": { "type": "Manual" }, "subnet_type": "Lan", "tags": [], "updated_at": "2026-01-15T10:30:00Z" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Tag": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/TagBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "color": "Green", "created_at": "2026-01-15T10:30:00Z", "description": "Production environment resources", "id": "550e8400-e29b-41d4-a716-44665544000a", "is_application": false, "name": "production", "organization_id": "550e8400-e29b-41d4-a716-446655440001", "updated_at": "2026-01-15T10:30:00Z" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_TestReachabilityResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response from a reachability test.", "required": [ "reachable" ], "properties": { "error": { "type": [ "string", "null" ], "description": "Error message if not reachable" }, "health": { "type": [ "boolean", "null" ], "description": "Health check result (only present when check_health was true)" }, "reachable": { "type": "boolean", "description": "Whether the TCP connection succeeded" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Topology": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/TopologyBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_User": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/UserBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_UserApiKey": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/UserApiKeyBase" }, { "type": "object", "required": [ "id", "updated_at", "created_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_UserApiKeyResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response for user API key creation/rotation\nContains the full API key record plus the plaintext key (shown only once)", "required": [ "api_key", "key" ], "properties": { "api_key": { "$ref": "#/components/schemas/UserApiKey" }, "key": { "type": "string", "description": "The plaintext API key - only returned once during creation or rotation" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Vec_BillingPlan": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "array", "items": { "oneOf": [ { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Community" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Free" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Starter" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Pro" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Team" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Business" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Enterprise" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Demo" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "CommercialSelfHosted" ] } } } ] } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Vec_Credential": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/CredentialBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Vec_DiscoveryUpdatePayload": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "array", "items": { "type": "object", "description": "Progress update from daemon to server during discovery", "required": [ "session_id", "daemon_id", "network_id", "phase", "discovery_type", "progress" ], "properties": { "daemon_id": { "type": "string", "format": "uuid" }, "discovery_id": { "type": [ "string", "null" ], "format": "uuid", "description": "The discovery configuration this session belongs to.\nAlways enriched server-side; daemons do not send this field." }, "discovery_type": { "$ref": "#/components/schemas/DiscoveryType" }, "error": { "type": [ "string", "null" ] }, "estimated_remaining_secs": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 }, "finished_at": { "type": [ "string", "null" ], "format": "date-time" }, "hosts_discovered": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 }, "network_id": { "type": "string", "format": "uuid" }, "phase": { "$ref": "#/components/schemas/DiscoveryPhase" }, "progress": { "type": "integer", "format": "int32", "minimum": 0 }, "session_id": { "type": "string", "format": "uuid" }, "started_at": { "type": [ "string", "null" ], "format": "date-time" } } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Vec_Invite": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/InviteBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_VersionInfo": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Version information for API compatibility checking", "required": [ "api_version", "server_version" ], "properties": { "api_version": { "type": "integer", "format": "int32", "description": "Current API version (integer, increments on breaking changes)", "minimum": 0 }, "min_compatible_client": { "type": [ "string", "null" ], "description": "Minimum client version that can use this API (optional, for future use)" }, "server_version": { "type": "string", "description": "Server version (semver)", "example": "0.12.10" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_Vlan": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/VlanBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_VlanDiscoveryResponse": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "object", "description": "Response for discovery upsert", "required": [ "vlans" ], "properties": { "vlans": { "type": "array", "items": { "$ref": "#/components/schemas/VlanDiscoveryResponseItem" }, "description": "Mapping of vlan_number → VLAN entity UUID" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "ApiResponse_u32": { "type": "object", "required": [ "success", "meta" ], "properties": { "data": { "type": "integer", "format": "int32", "minimum": 0 }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/ApiMeta" }, "success": { "type": "boolean" } } }, "BillingPlan": { "oneOf": [ { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Community" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Free" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Starter" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Pro" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Team" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Business" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Enterprise" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Demo" ] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/PlanConfig" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "CommercialSelfHosted" ] } } } ] } ] }, "BillingRate": { "type": "string", "enum": [ "Month", "Year" ] }, "Binding": { "allOf": [ { "$ref": "#/components/schemas/BindingBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "description": "Association between a service and a port / interface that the service is listening on", "example": { "created_at": "2026-04-18T15:49:08.301812Z", "id": "144f91ea-7572-4f3c-be4d-ea399ce641ff", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.301812Z" } }, "BindingBase": { "allOf": [ { "$ref": "#/components/schemas/BindingType" }, { "type": "object", "required": [ "service_id", "network_id" ], "properties": { "network_id": { "type": "string", "format": "uuid" }, "service_id": { "type": "string", "format": "uuid" } } } ], "description": "The base data for a Binding entity (everything except id, created_at, updated_at)" }, "BindingInput": { "oneOf": [ { "type": "object", "title": "IPAddress", "description": "Bind to an interface (service is present at this interface without a specific port)", "required": [ "id", "ip_address_id", "type" ], "properties": { "id": { "type": "string", "format": "uuid", "description": "Client-provided UUID for this binding" }, "ip_address_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "IPAddress" ] } } }, { "type": "object", "title": "Port", "description": "Bind to a port (optionally on a specific ip_address)", "required": [ "id", "port_id", "type" ], "properties": { "id": { "type": "string", "format": "uuid", "description": "Client-provided UUID for this binding" }, "ip_address_id": { "type": [ "string", "null" ], "format": "uuid", "description": "null = bind to all ip_addresses" }, "port_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "Port" ] } } } ], "description": "Input for creating or updating a binding within a service.\nUsed in both CreateHostRequest and UpdateHostRequest.\nClient must provide a UUID for the binding." }, "BindingType": { "oneOf": [ { "type": "object", "title": "IPAddress", "description": "IP address binding: Service is present at an IP address without a specific port.\nUsed for non-port-bound services like gateways. Conflicts with port bindings on the same IP address.", "required": [ "ip_address_id", "type" ], "properties": { "ip_address_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "IPAddress" ] } } }, { "type": "object", "title": "Port", "description": "Port binding: Service listens on a specific port, optionally on a specific IP address.\nIf `ip_address_id` is `null`, the service listens on this port across all IP addresses,\nwhich supersedes any specific-IP-address bindings for the same port.", "required": [ "port_id", "ip_address_id", "type" ], "properties": { "ip_address_id": { "type": [ "string", "null" ], "format": "uuid", "description": "The IP address this port binding applies to. If `null`, the binding applies to all\nIP addresses on the host (and supersedes specific-IP-address bindings for this port)." }, "port_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "Port" ] } } } ], "description": "The type of binding - either to an interface or to a port.\n\nBindings associate a service with network resources (ip_addresses/ports) on a host.\n\n## Validation Rules\n\n- All bindings must reference ports/interfaces that belong to the same host as the service.\n- Interface bindings conflict with port bindings on the same interface.\n- A port binding on all ip_addresses (`ip_address_id: null`) conflicts with any interface binding.\n- When a port binding with `ip_address_id: null` is created, it supersedes (removes) any\n existing specific-interface bindings for the same port." }, "BulkDeleteResponse": { "type": "object", "required": [ "deleted_count", "requested_count" ], "properties": { "deleted_count": { "type": "integer", "minimum": 0 }, "requested_count": { "type": "integer", "minimum": 0 } } }, "BulkTagRequest": { "type": "object", "description": "Request body for bulk tag operations", "required": [ "entity_type", "entity_ids", "tag_id" ], "properties": { "entity_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "The IDs of entities to modify" }, "entity_type": { "$ref": "#/components/schemas/EntityDiscriminants", "description": "The entity type (e.g., Host, Service, Subnet)" }, "tag_id": { "type": "string", "format": "uuid", "description": "The tag ID to add or remove" } } }, "BulkTagResponse": { "type": "object", "description": "Response for bulk tag operations", "required": [ "affected_count" ], "properties": { "affected_count": { "type": "integer", "description": "Number of entities affected", "minimum": 0 } } }, "ChangePlanPreview": { "type": "object", "required": [ "excess_hosts", "excess_networks", "excess_seats" ], "properties": { "excess_hosts": { "type": "integer", "format": "int64", "minimum": 0 }, "excess_networks": { "type": "integer", "format": "int64", "minimum": 0 }, "excess_seats": { "type": "integer", "format": "int64", "minimum": 0 } } }, "ChangePlanRequest": { "type": "object", "required": [ "plan", "rate" ], "properties": { "plan": { "$ref": "#/components/schemas/BillingPlan" }, "rate": { "$ref": "#/components/schemas/BillingRate" } } }, "CheckEmailRequest": { "type": "object", "description": "Check email availability request", "required": [ "email" ], "properties": { "email": { "type": "string", "format": "email" } } }, "Color": { "type": "string", "enum": [ "Pink", "Rose", "Red", "Amber", "Orange", "Green", "Emerald", "Teal", "Cyan", "Blue", "Indigo", "Purple", "Fuchsia", "Violet", "Sky", "Gray", "Lime", "Yellow" ] }, "ContainerType": { "type": "string", "enum": [ "Subnet", "ServiceCategory", "Application", "Root", "Host", "NestedTag", "NestedServiceCategory", "Hypervisor", "ContainerRuntime", "Stack", "TrunkPort", "VLAN", "PortOpStatus" ] }, "CreateBindingInput": { "oneOf": [ { "type": "object", "title": "IPAddress", "description": "Bind to an interface (service listens on all ports on this ip_address)", "required": [ "ip_address_id", "type" ], "properties": { "ip_address_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "IPAddress" ] } } }, { "type": "object", "title": "Port", "description": "Bind to a port (optionally on a specific ip_address)", "required": [ "port_id", "type" ], "properties": { "ip_address_id": { "type": [ "string", "null" ], "format": "uuid" }, "port_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "Port" ] } } } ], "description": "Input for creating a binding with a service.\n`service_id` and `network_id` are assigned by the server after the service is created." }, "CreateCheckoutRequest": { "type": "object", "required": [ "plan", "url" ], "properties": { "plan": { "$ref": "#/components/schemas/BillingPlan" }, "url": { "type": "string" } } }, "CreateHostRequest": { "type": "object", "description": "Request type for creating a host with its associated ip_addresses, ports, and services.\nServer assigns `host_id`, `network_id`, and `source` to all children.\nClient must provide UUIDs for all entities, enabling services to reference\nip_addresses/ports by ID in the same request.", "required": [ "name", "network_id", "tags" ], "properties": { "chassis_id": { "type": [ "string", "null" ] }, "credential_assignments": { "type": "array", "items": { "$ref": "#/components/schemas/CredentialAssignment" } }, "description": { "type": [ "string", "null" ] }, "hidden": { "type": "boolean" }, "hostname": { "type": [ "string", "null" ] }, "interfaces": { "type": "array", "items": { "$ref": "#/components/schemas/InterfaceInput" }, "description": "SNMP interface entries (ifTable data) - server assigns UUIDs" }, "ip_addresses": { "type": "array", "items": { "$ref": "#/components/schemas/IPAddressInput" }, "description": "Interfaces to create with this host (client provides UUIDs)" }, "management_url": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "ports": { "type": "array", "items": { "$ref": "#/components/schemas/PortInput" }, "description": "Ports to create with this host (client provides UUIDs)" }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceInput" }, "description": "Services to create with this host (can reference ip_addresses/ports by their UUIDs)" }, "sys_contact": { "type": [ "string", "null" ] }, "sys_descr": { "type": [ "string", "null" ] }, "sys_location": { "type": [ "string", "null" ] }, "sys_object_id": { "type": [ "string", "null" ] }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostVirtualization" } ] } }, "example": { "credential_assignments": [], "description": "Primary web server", "hidden": false, "hostname": "web-server-01.local", "interfaces": [], "ip_addresses": [ { "id": "550e8400-e29b-41d4-a716-446655440005", "ip_address": "192.168.1.100", "mac_address": "DE:AD:BE:EF:12:34", "name": "eth0", "position": 0, "subnet_id": "550e8400-e29b-41d4-a716-446655440004" } ], "name": "web-server-01", "network_id": "550e8400-e29b-41d4-a716-446655440002", "ports": [ { "id": "550e8400-e29b-41d4-a716-446655440006", "number": 80, "protocol": "Tcp" } ], "services": [ { "bindings": [ { "id": "550e8400-e29b-41d4-a716-446655440009", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "port_id": "550e8400-e29b-41d4-a716-446655440006", "type": "Port" } ], "id": "550e8400-e29b-41d4-a716-446655440007", "name": "nginx", "position": 0, "service_definition": "Zabbix Agent", "tags": [], "virtualization": null } ], "tags": [], "virtualization": null } }, "CreateInviteRequest": { "type": "object", "required": [ "permissions", "network_ids" ], "properties": { "expiration_hours": { "type": [ "integer", "null" ], "format": "int64" }, "network_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "permissions": { "$ref": "#/components/schemas/UserOrgPermissions" }, "send_to": { "type": [ "string", "null" ] } } }, "CreateServiceRequest": { "type": "object", "description": "Request type for creating a service.\nServer assigns `id`, `created_at`, `updated_at`, and `source`.\nServer also assigns `service_id` and `network_id` to all bindings.", "required": [ "host_id", "network_id", "service_definition", "name", "tags" ], "properties": { "bindings": { "type": "array", "items": { "$ref": "#/components/schemas/CreateBindingInput" }, "description": "Bindings to create with the service.\n`service_id` and `network_id` are assigned by the server." }, "host_id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "service_definition": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServiceVirtualization" } ] } } }, "CreateUpdateShareRequest": { "type": "object", "required": [ "share" ], "properties": { "password": { "type": [ "string", "null" ] }, "share": { "$ref": "#/components/schemas/Share" } } }, "Credential": { "allOf": [ { "$ref": "#/components/schemas/CredentialBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "CredentialAssignment": { "type": "object", "description": "A credential assigned to a host, optionally limited to specific ip_addresses.", "required": [ "credential_id", "ip_address_ids" ], "properties": { "credential_id": { "type": "string", "format": "uuid" }, "ip_address_ids": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" }, "description": "Interface IDs to limit this credential to. None = all host ip_addresses." } } }, "CredentialBase": { "type": "object", "required": [ "organization_id", "name", "credential_type", "tags" ], "properties": { "credential_type": { "$ref": "#/components/schemas/CredentialType" }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "target_ips": { "type": [ "array", "null" ], "items": { "type": "string" }, "description": "Ephemeral bootstrap IPs for pre-discovery credential resolution.\nCleared automatically when the next scan dispatches." } } }, "CredentialOrderField": { "type": "string", "enum": [ "created_at", "name", "updated_at" ] }, "CredentialType": { "oneOf": [ { "type": "object", "description": "SNMPv2c community string for querying network devices", "required": [ "community", "type" ], "properties": { "community": { "$ref": "#/components/schemas/SecretValue" }, "type": { "type": "string", "enum": [ "SnmpV2c" ] } } }, { "type": "object", "description": "Docker API proxy credentials. Target IP determined from host ip_addresses at scan time.", "required": [ "type" ], "properties": { "path": { "type": [ "string", "null" ], "description": "Optional URL path prefix (e.g. \"/v1.43\")" }, "port": { "type": "integer", "format": "int32", "description": "Port for the Docker API proxy (default 2375)", "minimum": 0 }, "ssl_cert": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/FileOrInline", "description": "PEM-encoded public certificate — inline or file path on daemon host" } ] }, "ssl_chain": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/FileOrInline", "description": "PEM-encoded CA chain — inline or file path on daemon host" } ] }, "ssl_key": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/SecretValue", "description": "Private key — inline PEM content or file path on daemon host" } ] }, "type": { "type": "string", "enum": [ "DockerProxy" ] } } }, { "type": "object", "description": "Local Docker socket access. Auto-injected by daemon when socket is available.\nNot user-selectable — managed automatically from daemon capabilities.", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "DockerSocket" ] } } } ], "description": "Universal credential type — tagged enum stored as JSONB.\nEach variant represents a different credential protocol/method." }, "Daemon": { "allOf": [ { "$ref": "#/components/schemas/DaemonBase" }, { "type": "object", "required": [ "id", "updated_at", "created_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "DaemonApiKey": { "allOf": [ { "$ref": "#/components/schemas/DaemonApiKeyBase" }, { "type": "object", "required": [ "id", "updated_at", "created_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "DaemonApiKeyBase": { "type": "object", "required": [ "key", "name", "last_used", "network_id", "tags" ], "properties": { "expires_at": { "type": [ "string", "null" ], "format": "date-time" }, "is_enabled": { "type": "boolean" }, "key": { "type": "string", "readOnly": true }, "last_used": { "type": [ "string", "null" ], "format": "date-time", "readOnly": true }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "DaemonApiKeyResponse": { "type": "object", "required": [ "api_key", "key" ], "properties": { "api_key": { "$ref": "#/components/schemas/DaemonApiKey" }, "key": { "type": "string" } } }, "DaemonBase": { "type": "object", "required": [ "host_id", "network_id", "url", "capabilities", "mode", "name", "tags", "user_id" ], "properties": { "api_key_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Foreign key to API key used for ServerPoll authentication.\nNULL for DaemonPoll daemons or those not yet linked to a key." }, "capabilities": { "$ref": "#/components/schemas/DaemonCapabilities" }, "host_id": { "type": "string", "format": "uuid" }, "is_unreachable": { "type": "boolean", "description": "Whether the daemon is unreachable (for ServerPoll circuit breaker).\nSet to true after repeated polling failures, reset via retry-connection endpoint." }, "last_seen": { "type": [ "string", "null" ], "format": "date-time", "description": "Timestamp of last successful contact with daemon.\nNULL for provisioned ServerPoll daemons that haven't been contacted yet.", "readOnly": true }, "mode": { "$ref": "#/components/schemas/DaemonMode" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "standby": { "type": "boolean", "description": "Whether the daemon is on standby due to inactivity (no discovery in 30 days).", "readOnly": true }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "url": { "type": "string", "readOnly": true }, "user_id": { "type": "string", "format": "uuid", "description": "User responsible for maintaining this daemon" }, "version": { "type": [ "string", "null" ], "description": "Daemon software version (semver format)" } } }, "DaemonCapabilities": { "type": "object", "description": "Daemon capabilities", "required": [ "interfaced_subnet_ids" ], "properties": { "has_docker_socket": { "type": "boolean" }, "interfaced_subnet_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "DaemonHeartbeatPayload": { "type": "object", "description": "Legacy heartbeat payload for backwards compatibility with pre-v0.14.0 daemons.\nOld daemons call POST /api/daemons/{id}/heartbeat with this payload.", "required": [ "url", "name", "mode" ], "properties": { "mode": { "$ref": "#/components/schemas/DaemonMode" }, "name": { "type": "string" }, "url": { "type": "string" } } }, "DaemonMode": { "type": "string", "description": "Daemon operating mode that determines the communication pattern.\n\n- **DaemonPoll** (formerly \"Pull\"): Daemon makes outbound connections to the server.\n The daemon registers itself and polls for work. Best for daemons behind NAT/firewall.\n\n- **ServerPoll** (formerly \"Push\"): Server makes connections to the daemon.\n Server polls daemon for status and discovery results. Best for DMZ deployments\n where daemon cannot make outbound connections.", "enum": [ "server_poll", "daemon_poll" ] }, "DaemonOrderField": { "type": "string", "description": "Fields that daemons can be ordered/grouped by.", "enum": [ "created_at", "name", "last_seen", "updated_at", "network_id" ] }, "DaemonRegistrationRequest": { "type": "object", "description": "Daemon registration request from daemon to server", "required": [ "daemon_id", "network_id", "name", "mode", "capabilities" ], "properties": { "capabilities": { "$ref": "#/components/schemas/DaemonCapabilities" }, "credential_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Credential IDs to assign to daemon's host during registration." }, "daemon_id": { "type": "string", "format": "uuid" }, "mode": { "$ref": "#/components/schemas/DaemonMode" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "url": { "type": [ "string", "null" ], "description": "URL is ignored by server - kept for backwards compat with old daemons.\nURL is only set via admin provisioning for ServerPoll daemons." }, "user_id": { "type": "string", "format": "uuid", "description": "User responsible for maintaining this daemon (from frontend install command)\nOptional for backwards compat with old daemons - defaults to nil UUID" }, "version": { "type": [ "string", "null" ], "description": "Daemon software version (optional for backwards compat with old daemons)" } } }, "DaemonRegistrationResponse": { "type": "object", "description": "Daemon registration response from server to daemon", "required": [ "daemon", "host_id" ], "properties": { "daemon": { "$ref": "#/components/schemas/Daemon" }, "host_id": { "type": "string", "format": "uuid" }, "server_capabilities": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServerCapabilities", "description": "Server capabilities (returned if daemon sends version info)" } ] } } }, "DaemonResponse": { "allOf": [ { "$ref": "#/components/schemas/DaemonBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at", "version_status" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "version_status": { "$ref": "#/components/schemas/DaemonVersionStatus", "description": "Computed version status including health and warnings" } } } ], "description": "Daemon response for UI including computed version status" }, "DaemonStartupRequest": { "type": "object", "description": "Sent by daemon on startup to report version", "required": [ "daemon_version" ], "properties": { "daemon_version": { "type": "string", "description": "Daemon software version (semver format)" } } }, "DaemonStatus": { "type": "object", "description": "Lightweight daemon status for polling responses.", "required": [ "name", "mode" ], "properties": { "capabilities": { "$ref": "#/components/schemas/DaemonCapabilities", "description": "Backwards compat: pre-v0.15.0 daemons send capabilities instead of interfaced_subnets." }, "has_docker_socket": { "type": "boolean", "description": "Whether the daemon has access to a Docker socket." }, "interfaced_subnets": { "type": "array", "items": { "$ref": "#/components/schemas/Subnet" }, "description": "Subnets detected from daemon's network ip_addresses. Server resolves these\nvia SubnetService::create (create-or-match by CIDR) to get real IDs.\nv0.15.0+ daemons populate this; pre-v0.15.0 daemons leave it empty." }, "mode": { "$ref": "#/components/schemas/DaemonMode" }, "name": { "type": "string" }, "ready_for_work": { "type": "boolean", "description": "Whether the daemon can accept a new discovery session.\nBoth DaemonPoll and ServerPoll use this to avoid dispatching work to a busy daemon." }, "url": { "type": [ "string", "null" ], "description": "URL is not used by server - kept for backwards compat.\nServer never updates daemon URL from status (URL is set during provisioning)." }, "version": { "type": [ "string", "null" ], "description": "Daemon software version (semver format)" } } }, "DaemonVersionStatus": { "type": "object", "description": "Daemon version status including health and any warnings", "required": [ "status" ], "properties": { "status": { "$ref": "#/components/schemas/VersionHealthStatus" }, "supports_unified_discovery": { "type": "boolean" }, "version": { "type": [ "string", "null" ] }, "warnings": { "type": "array", "items": { "$ref": "#/components/schemas/DeprecationWarning" } } } }, "DashboardSummary": { "type": "object", "description": "Dashboard summary response", "required": [ "networks", "daemons", "recent_discoveries", "plan_usage" ], "properties": { "daemons": { "type": "array", "items": { "$ref": "#/components/schemas/DaemonResponse" } }, "networks": { "type": "array", "items": { "$ref": "#/components/schemas/NetworkSummary" } }, "plan_usage": { "$ref": "#/components/schemas/PlanUsage" }, "recent_discoveries": { "type": "array", "items": { "$ref": "#/components/schemas/Discovery" } } } }, "Dependency": { "allOf": [ { "$ref": "#/components/schemas/DependencyBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "color": "Blue", "created_at": "2026-01-15T10:30:00Z", "dependency_type": "RequestPath", "description": "HTTP/HTTPS services dependency", "edge_style": "Bezier", "id": "550e8400-e29b-41d4-a716-446655440008", "members": { "service_ids": [], "type": "Services" }, "name": "Web Services", "network_id": "550e8400-e29b-41d4-a716-446655440002", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z" } }, "DependencyBase": { "type": "object", "required": [ "name", "network_id", "dependency_type", "members", "color", "edge_style", "tags" ], "properties": { "color": { "$ref": "#/components/schemas/Color" }, "dependency_type": { "$ref": "#/components/schemas/DependencyType" }, "description": { "type": [ "string", "null" ] }, "edge_style": { "$ref": "#/components/schemas/EdgeStyle" }, "members": { "$ref": "#/components/schemas/DependencyMembers", "description": "Members of this dependency: either service IDs or binding IDs." }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "source": { "$ref": "#/components/schemas/EntitySource", "description": "Will be automatically set to Manual for creation through API" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "DependencyMembers": { "oneOf": [ { "type": "object", "description": "Application-level only. Ordered list of service IDs.", "required": [ "service_ids", "type" ], "properties": { "service_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "type": { "type": "string", "enum": [ "Services" ] } } }, { "type": "object", "description": "Full L3 detail. Ordered list of binding IDs (one per service in the chain).", "required": [ "binding_ids", "type" ], "properties": { "binding_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "type": { "type": "string", "enum": [ "Bindings" ] } } } ], "description": "The members of a dependency: either service-level or binding-level.\nBindings are all-or-nothing: either every position has a binding (full L3 detail)\nor none do (Application-level only)." }, "DependencyOrderField": { "type": "string", "description": "Fields that dependencies can be ordered/grouped by.", "enum": [ "created_at", "name", "dependency_type", "updated_at", "network_id" ] }, "DependencyType": { "type": "string", "enum": [ "RequestPath", "HubAndSpoke" ] }, "DeploymentType": { "type": "string", "enum": [ "cloud", "commercial", "community" ] }, "DeprecationSeverity": { "type": "string", "description": "Severity level for deprecation warnings", "enum": [ "Info", "Warning", "Critical" ] }, "DeprecationWarning": { "type": "object", "description": "Deprecation warning for daemon version", "required": [ "message", "severity" ], "properties": { "message": { "type": "string" }, "severity": { "$ref": "#/components/schemas/DeprecationSeverity" }, "sunset_date": { "type": [ "string", "null" ] } } }, "Discovery": { "allOf": [ { "$ref": "#/components/schemas/DiscoveryBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "force_full_scan": { "type": "boolean", "description": "When true, the next scan will be a full port scan regardless of interval" }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "pending_credential_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Credential IDs to include in the next scan's credential_mappings.\nSet by the discovery edit modal, cleared after each scan completes." }, "scan_count": { "type": "integer", "format": "int32", "description": "Number of completed scans (incremented by server on session completion)", "readOnly": true, "minimum": 0 }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "DiscoveryBase": { "type": "object", "required": [ "discovery_type", "run_type", "name", "daemon_id", "network_id", "tags" ], "properties": { "daemon_id": { "type": "string", "format": "uuid" }, "discovery_type": { "$ref": "#/components/schemas/DiscoveryType" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "run_type": { "$ref": "#/components/schemas/RunType" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "DiscoveryHostRequest": { "type": "object", "description": "Request type for daemon discovery - accepts full entities with IDs.\nUsed internally by daemons for host creation/upsert, NOT the external API.\nThis supports the discovery workflow where daemons manage entity IDs.\n\n## Backwards compatibility (daemons < v0.16.0)\n\nPre-v0.16.0 daemons send the old field layout:\n - `interfaces` → IPAddress data (now `ip_addresses`)\n - `if_entries` → SNMP Interface data (now `interfaces`)\n\nThe custom deserializer detects the old layout (missing `ip_addresses` field)\nand remaps fields automatically. This can be removed once all daemons are ≥ v0.16.0.", "required": [ "host", "ip_addresses", "ports", "services" ], "properties": { "host": { "$ref": "#/components/schemas/Host" }, "interfaces": { "type": "array", "items": { "$ref": "#/components/schemas/Interface" }, "description": "SNMP interface entries (ifTable data) - optional, populated when SNMP is enabled." }, "ip_addresses": { "type": "array", "items": { "$ref": "#/components/schemas/IPAddress" } }, "ports": { "type": "array", "items": { "$ref": "#/components/schemas/Port" } }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/Service" } }, "subnets": { "type": "array", "items": { "$ref": "#/components/schemas/Subnet" }, "description": "Integration-derived subnets (e.g., Docker bridge networks) — created during\ncreate_with_children after service dedup so virtualization.service_id is correct." } } }, "DiscoveryMetadata": { "allOf": [ { "$ref": "#/components/schemas/DiscoveryType" }, { "type": "object", "required": [ "daemon_id", "date" ], "properties": { "daemon_id": { "type": "string", "format": "uuid" }, "date": { "type": "string", "format": "date-time" } } } ] }, "DiscoveryPhase": { "type": "string", "enum": [ "Queued", "Pending", "Starting", "Started", "Scanning", "Complete", "Failed", "Cancelled" ] }, "DiscoveryProtocol": { "type": "string", "description": "Protocol that discovered the physical link between network devices", "enum": [ "LLDP", "CDP" ] }, "DiscoveryType": { "oneOf": [ { "type": "object", "title": "SelfReport", "required": [ "host_id", "type" ], "properties": { "host_id": { "type": "string", "format": "uuid" }, "type": { "type": "string", "enum": [ "SelfReport" ] } } }, { "type": "object", "title": "Network", "required": [ "subnet_ids", "host_naming_fallback", "type" ], "properties": { "host_naming_fallback": { "$ref": "#/components/schemas/HostNamingFallback" }, "snmp_credentials": { "type": "object", "description": "SNMP credentials for querying devices during discovery\nServer builds this mapping before initiating discovery" }, "subnet_ids": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" } }, "type": { "type": "string", "enum": [ "Network" ] } } }, { "type": "object", "title": "Docker", "required": [ "host_id", "host_naming_fallback", "type" ], "properties": { "host_id": { "type": "string", "format": "uuid" }, "host_naming_fallback": { "$ref": "#/components/schemas/HostNamingFallback" }, "type": { "type": "string", "enum": [ "Docker" ] } } }, { "type": "object", "title": "Unified", "required": [ "host_id", "subnet_ids", "host_naming_fallback", "type" ], "properties": { "host_id": { "type": "string", "format": "uuid", "description": "ID of the host that the daemon is running on" }, "host_naming_fallback": { "$ref": "#/components/schemas/HostNamingFallback", "description": "Fallback strategy for naming discovered hosts" }, "scan_settings": { "$ref": "#/components/schemas/ScanSettings", "description": "Per-discovery scan performance settings" }, "subnet_ids": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" }, "description": "Subnets to scan. None = scan all interfaced subnets." }, "type": { "type": "string", "enum": [ "Unified" ] } } } ] }, "DiscoveryUpdatePayload": { "type": "object", "description": "Progress update from daemon to server during discovery", "required": [ "session_id", "daemon_id", "network_id", "phase", "discovery_type", "progress" ], "properties": { "daemon_id": { "type": "string", "format": "uuid" }, "discovery_id": { "type": [ "string", "null" ], "format": "uuid", "description": "The discovery configuration this session belongs to.\nAlways enriched server-side; daemons do not send this field." }, "discovery_type": { "$ref": "#/components/schemas/DiscoveryType" }, "error": { "type": [ "string", "null" ] }, "estimated_remaining_secs": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 }, "finished_at": { "type": [ "string", "null" ], "format": "date-time" }, "hosts_discovered": { "type": [ "integer", "null" ], "format": "int32", "minimum": 0 }, "network_id": { "type": "string", "format": "uuid" }, "phase": { "$ref": "#/components/schemas/DiscoveryPhase" }, "progress": { "type": "integer", "format": "int32", "minimum": 0 }, "session_id": { "type": "string", "format": "uuid" }, "started_at": { "type": [ "string", "null" ], "format": "date-time" } } }, "DockerSubnetVirtualization": { "type": "object", "required": [ "service_id" ], "properties": { "service_id": { "type": "string", "format": "uuid", "description": "The Docker daemon service that owns this bridge network.\nDifferent Docker daemons on different hosts = distinct bridge subnets." } } }, "DockerVirtualization": { "type": "object", "required": [ "service_id" ], "properties": { "compose_project": { "type": [ "string", "null" ] }, "container_id": { "type": [ "string", "null" ] }, "container_name": { "type": [ "string", "null" ] }, "service_id": { "type": "string", "format": "uuid" } } }, "Edge": { "allOf": [ { "$ref": "#/components/schemas/EdgeType" }, { "type": "object", "required": [ "id", "source", "target", "label", "source_handle", "target_handle", "is_multi_hop" ], "properties": { "id": { "type": "string", "format": "uuid" }, "is_multi_hop": { "type": "boolean" }, "label": { "type": [ "string", "null" ] }, "source": { "type": "string", "format": "uuid" }, "source_handle": { "$ref": "#/components/schemas/EdgeHandle" }, "target": { "type": "string", "format": "uuid" }, "target_handle": { "$ref": "#/components/schemas/EdgeHandle" }, "view_config": { "$ref": "#/components/schemas/EdgeViewConfig" } } } ] }, "EdgeDefaultVisibility": { "type": "string", "description": "Whether an edge is visible by default or hidden behind a toggle", "enum": [ "visible", "hidden" ] }, "EdgeHandle": { "type": "string", "enum": [ "Top", "Bottom", "Left", "Right" ] }, "EdgeHighlightBehavior": { "type": "string", "description": "Controls when an edge contributes to node highlighting on selection", "enum": [ "when_visible", "always", "never" ] }, "EdgeStroke": { "type": "string", "description": "Visual stroke style for an edge", "enum": [ "solid", "dashed" ] }, "EdgeStyle": { "type": "string", "enum": [ "Straight", "SmoothStep", "Bezier" ] }, "EdgeType": { "oneOf": [ { "type": "object", "required": [ "host_id", "edge_type" ], "properties": { "edge_type": { "type": "string", "enum": [ "SameHost" ] }, "host_id": { "type": "string", "format": "uuid" } } }, { "type": "object", "required": [ "hypervisor_service_id", "edge_type" ], "properties": { "edge_type": { "type": "string", "enum": [ "Hypervisor" ] }, "hypervisor_service_id": { "type": "string", "format": "uuid" } } }, { "type": "object", "required": [ "host_id", "service_id", "edge_type" ], "properties": { "edge_type": { "type": "string", "enum": [ "ContainerRuntime" ] }, "host_id": { "type": "string", "format": "uuid" }, "service_id": { "type": "string", "format": "uuid" } } }, { "type": "object", "required": [ "dependency_id", "source_id", "target_id", "edge_type" ], "properties": { "dependency_id": { "type": "string", "format": "uuid" }, "edge_type": { "type": "string", "enum": [ "RequestPath" ] }, "source_id": { "type": "string", "format": "uuid" }, "target_id": { "type": "string", "format": "uuid" } } }, { "type": "object", "required": [ "dependency_id", "source_id", "target_id", "edge_type" ], "properties": { "dependency_id": { "type": "string", "format": "uuid" }, "edge_type": { "type": "string", "enum": [ "HubAndSpoke" ] }, "source_id": { "type": "string", "format": "uuid" }, "target_id": { "type": "string", "format": "uuid" } } }, { "type": "object", "description": "Physical link discovered via LLDP/CDP neighbor discovery", "required": [ "source_entity_id", "target_entity_id", "protocol", "edge_type" ], "properties": { "edge_type": { "type": "string", "enum": [ "PhysicalLink" ] }, "protocol": { "$ref": "#/components/schemas/DiscoveryProtocol" }, "source_entity_id": { "type": "string", "format": "uuid" }, "target_entity_id": { "type": "string", "format": "uuid" } } } ] }, "EdgeTypeDiscriminants": { "type": "string", "enum": [ "SameHost", "Hypervisor", "ContainerRuntime", "RequestPath", "HubAndSpoke", "PhysicalLink" ] }, "EdgeViewConfig": { "oneOf": [ { "type": "object", "description": "Edge is not available in this view", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "disabled" ] } } }, { "type": "object", "description": "Edge is active in this view with specific properties", "required": [ "affects_layout", "default_visibility", "stroke", "highlight_behavior", "will_target_container", "show_directionality", "type" ], "properties": { "affects_layout": { "type": "boolean", "description": "Whether ELK should use this edge for layout positioning" }, "default_visibility": { "$ref": "#/components/schemas/EdgeDefaultVisibility", "description": "Whether the edge is shown by default or hidden behind a toggle" }, "highlight_behavior": { "$ref": "#/components/schemas/EdgeHighlightBehavior", "description": "When this edge contributes to node highlighting on selection" }, "show_directionality": { "type": "boolean", "description": "Whether this edge should show directional animation when highlighted" }, "stroke": { "$ref": "#/components/schemas/EdgeStroke", "description": "Visual stroke style" }, "type": { "type": "string", "enum": [ "active" ] }, "will_target_container": { "type": "boolean", "description": "Whether this edge should be elevated to target an accepting container\ninstead of the element inside it" } } } ], "description": "Per-view configuration for an edge: disabled (not in this view) or active with properties" }, "ElementEntityType": { "oneOf": [ { "type": "object", "required": [ "subnet_id", "element_type" ], "properties": { "element_type": { "type": "string", "enum": [ "IPAddress" ] }, "ip_address_id": { "type": [ "string", "null" ], "format": "uuid" }, "subnet_id": { "type": "string", "format": "uuid" } } }, { "type": "object", "required": [ "element_type" ], "properties": { "element_type": { "type": "string", "enum": [ "Service" ] } } }, { "type": "object", "required": [ "element_type" ], "properties": { "element_type": { "type": "string", "enum": [ "Host" ] } } }, { "type": "object", "required": [ "interface_id", "element_type" ], "properties": { "element_type": { "type": "string", "enum": [ "Interface" ] }, "interface_id": { "type": "string", "format": "uuid" } } } ] }, "EmailInstallCommandRequest": { "type": "object", "description": "Request body for emailing an install command to the authenticated user.", "required": [ "install_command", "os" ], "properties": { "install_command": { "type": "string" }, "os": { "type": "string" } } }, "EnterpriseInquiryRequest": { "type": "object", "description": "Enterprise plan inquiry request", "required": [ "email", "name", "company", "team_size", "message" ], "properties": { "company": { "type": "string", "description": "Company name" }, "email": { "type": "string", "description": "Contact email" }, "message": { "type": "string", "description": "Message/use case description" }, "name": { "type": "string", "description": "Contact name" }, "network_count": { "type": [ "integer", "null" ], "format": "int64", "description": "Number of networks/sites" }, "plan_type": { "type": [ "string", "null" ], "description": "Plan type being inquired about" }, "team_size": { "type": "string", "description": "Team/company size: 1-10, 11-25, 26-50, 51-100, 101-250, 251-500, 501-1000, 1001+" }, "urgency": { "type": [ "string", "null" ], "description": "Urgency: immediately, 1-3 months, 3-6 months, exploring" } } }, "EntityDiscriminants": { "type": "string", "enum": [ "Organization", "Invite", "Share", "Network", "DaemonApiKey", "UserApiKey", "User", "Tag", "Discovery", "Daemon", "Host", "Service", "Port", "Binding", "IPAddress", "Interface", "Credential", "Subnet", "Vlan", "Dependency", "Topology", "Unknown" ] }, "EntitySource": { "oneOf": [ { "type": "object", "title": "Manual", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Manual" ] } } }, { "type": "object", "title": "System", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "System" ] } } }, { "type": "object", "title": "Discovery", "required": [ "metadata", "type" ], "properties": { "metadata": { "type": "array", "items": { "$ref": "#/components/schemas/DiscoveryMetadata" } }, "type": { "type": "string", "enum": [ "Discovery" ] } } }, { "type": "object", "title": "DiscoveryWithMatch", "required": [ "metadata", "details", "type" ], "properties": { "details": { "$ref": "#/components/schemas/MatchDetails" }, "metadata": { "type": "array", "items": { "$ref": "#/components/schemas/DiscoveryMetadata" } }, "type": { "type": "string", "enum": [ "DiscoveryWithMatch" ] } } }, { "type": "object", "title": "Unknown", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Unknown" ] } } } ] }, "FileOrInline": { "oneOf": [ { "type": "object", "required": [ "value", "mode" ], "properties": { "mode": { "type": "string", "enum": [ "Inline" ] }, "value": { "type": "string" } } }, { "type": "object", "required": [ "path", "mode" ], "properties": { "mode": { "type": "string", "enum": [ "FilePath" ] }, "path": { "type": "string" } } } ], "description": "Non-secret value that can be inline content or a file path on daemon host." }, "ForgotPasswordRequest": { "type": "object", "required": [ "email" ], "properties": { "email": { "type": "string", "format": "email" } } }, "Host": { "allOf": [ { "$ref": "#/components/schemas/HostBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "created_at": "2026-01-15T10:30:00Z", "credential_assignments": [], "description": "Primary web server", "hidden": false, "hostname": "web-server-01.local", "id": "550e8400-e29b-41d4-a716-446655440003", "name": "web-server-01", "network_id": "550e8400-e29b-41d4-a716-446655440002", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } }, "HostBase": { "type": "object", "description": "Base data for a Host entity (stored in database).\nChild entities (ip_addresses, ports, services) are stored in their own tables\nand queried by `host_id`. They are NOT stored on the host.", "required": [ "name", "network_id", "hostname", "description", "source", "virtualization", "hidden", "tags", "credential_assignments" ], "properties": { "chassis_id": { "type": [ "string", "null" ], "description": "LLDP lldpLocChassisId - globally unique device identifier for deduplication" }, "credential_assignments": { "type": "array", "items": { "$ref": "#/components/schemas/CredentialAssignment" }, "description": "Credential assignments for this host (hydrated from junction table)." }, "description": { "type": [ "string", "null" ] }, "hidden": { "type": "boolean" }, "hostname": { "type": [ "string", "null" ] }, "management_url": { "type": [ "string", "null" ], "description": "URL for device management interface (manual or discovered)" }, "manufacturer": { "type": [ "string", "null" ], "description": "ENTITY-MIB entPhysicalMfgName - hardware manufacturer" }, "model": { "type": [ "string", "null" ], "description": "ENTITY-MIB entPhysicalModelName - hardware model" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "serial_number": { "type": [ "string", "null" ], "description": "ENTITY-MIB entPhysicalSerialNum - hardware serial number" }, "source": { "$ref": "#/components/schemas/EntitySource" }, "sys_contact": { "type": [ "string", "null" ], "description": "SNMP sysContact.0 - admin contact info" }, "sys_descr": { "type": [ "string", "null" ], "description": "SNMP sysDescr.0 - full system description" }, "sys_location": { "type": [ "string", "null" ], "description": "SNMP sysLocation.0 - physical location" }, "sys_name": { "type": [ "string", "null" ], "description": "SNMP sysName.0 - administratively-assigned hostname" }, "sys_object_id": { "type": [ "string", "null" ], "description": "SNMP sysObjectID.0 - vendor OID for device identification" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostVirtualization" } ] } } }, "HostNamingFallback": { "type": "string", "enum": [ "Ip", "BestService" ] }, "HostOrderField": { "type": "string", "description": "Fields that hosts can be ordered/grouped by.", "enum": [ "created_at", "name", "hostname", "updated_at", "virtualized_by", "network_id", "interface_ip" ] }, "HostResponse": { "type": "object", "description": "Response type for host endpoints.\nIncludes children (ip_addresses, ports, services, interfaces).", "required": [ "id", "created_at", "updated_at", "name", "network_id", "source", "hidden", "tags", "ip_addresses", "ports", "services", "interfaces" ], "properties": { "chassis_id": { "type": [ "string", "null" ] }, "created_at": { "type": "string", "format": "date-time" }, "credential_assignments": { "type": "array", "items": { "$ref": "#/components/schemas/CredentialAssignment" } }, "description": { "type": [ "string", "null" ] }, "hidden": { "type": "boolean" }, "hostname": { "type": [ "string", "null" ] }, "id": { "type": "string", "format": "uuid" }, "interfaces": { "type": "array", "items": { "$ref": "#/components/schemas/Interface" }, "description": "SNMP ifTable entries" }, "ip_addresses": { "type": "array", "items": { "$ref": "#/components/schemas/IPAddress" } }, "management_url": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "ports": { "type": "array", "items": { "$ref": "#/components/schemas/Port" } }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/Service" } }, "source": { "$ref": "#/components/schemas/EntitySource" }, "sys_contact": { "type": [ "string", "null" ] }, "sys_descr": { "type": [ "string", "null" ] }, "sys_location": { "type": [ "string", "null" ] }, "sys_object_id": { "type": [ "string", "null" ] }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "updated_at": { "type": "string", "format": "date-time" }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostVirtualization" } ] } }, "example": { "created_at": "2026-01-15T10:30:00Z", "credential_assignments": [], "description": "Primary web server", "hidden": false, "hostname": "web-server-01.local", "id": "550e8400-e29b-41d4-a716-446655440003", "interfaces": [ { "admin_status": "Up", "cdp_address": null, "cdp_device_id": null, "cdp_platform": null, "cdp_port_id": null, "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-44665544000f", "if_alias": "Uplink to Core Switch", "if_descr": "GigabitEthernet0/1", "if_index": 1, "if_name": "Gi0/1", "if_type": 6, "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "lldp_chassis_id": null, "lldp_mgmt_addr": null, "lldp_port_desc": null, "lldp_port_id": null, "lldp_sys_desc": null, "lldp_sys_name": null, "mac_address": "DE:AD:BE:EF:CA:FE", "neighbor": null, "network_id": "550e8400-e29b-41d4-a716-446655440002", "oper_status": "Up", "speed_bps": 1000000000, "updated_at": "2026-01-15T10:30:00Z" } ], "ip_addresses": [ { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440005", "ip_address": "192.168.1.100", "mac_address": "DE:AD:BE:EF:CA:FE", "name": "eth0", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "subnet_id": "550e8400-e29b-41d4-a716-446655440004", "updated_at": "2026-01-15T10:30:00Z" } ], "name": "web-server-01", "network_id": "550e8400-e29b-41d4-a716-446655440002", "ports": [ { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440006", "network_id": "550e8400-e29b-41d4-a716-446655440002", "number": 80, "protocol": "Tcp", "type": "Http", "updated_at": "2026-01-15T10:30:00Z" } ], "services": [ { "bindings": [ { "created_at": "2026-04-18T15:49:08.300750Z", "id": "6eb5e1f9-7c1f-4935-8d12-77a900da9986", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.300750Z" } ], "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440007", "name": "nginx", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "service_definition": "Zabbix Agent", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } ], "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } }, "HostVirtualization": { "oneOf": [ { "type": "object", "title": "Proxmox", "required": [ "details", "type" ], "properties": { "details": { "$ref": "#/components/schemas/ProxmoxVirtualization" }, "type": { "type": "string", "enum": [ "Proxmox" ] } } } ], "title": "HostVirtualization" }, "IPAddress": { "allOf": [ { "$ref": "#/components/schemas/IPAddressBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440005", "ip_address": "192.168.1.100", "mac_address": "DE:AD:BE:EF:CA:FE", "name": "eth0", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "subnet_id": "550e8400-e29b-41d4-a716-446655440004", "updated_at": "2026-01-15T10:30:00Z" } }, "IPAddressBase": { "type": "object", "required": [ "network_id", "host_id", "subnet_id", "ip_address", "name" ], "properties": { "host_id": { "type": "string", "format": "uuid" }, "ip_address": { "type": "string" }, "mac_address": { "type": [ "string", "null" ], "description": "MAC address discovered from ARP, SNMP, or Docker - immutable once set" }, "name": { "type": [ "string", "null" ] }, "network_id": { "type": "string", "format": "uuid" }, "position": { "type": "integer", "format": "int32", "description": "Position of this IP address in the host's IP address list (for ordering)" }, "subnet_id": { "type": "string", "format": "uuid" } } }, "IPAddressInput": { "type": "object", "description": "Input for creating or updating an interface.\nUsed in both CreateHostRequest and UpdateHostRequest.\nClient must provide a UUID for the interface.", "required": [ "id", "subnet_id", "ip_address" ], "properties": { "id": { "type": "string", "format": "uuid", "description": "Client-provided UUID for this interface" }, "ip_address": { "type": "string" }, "mac_address": { "type": [ "string", "null" ] }, "name": { "type": [ "string", "null" ] }, "position": { "type": [ "integer", "null" ], "format": "int32", "description": "Position in the host's interface list (for ordering).\nIf omitted on create: appends to end of list.\nIf omitted on update: existing ip_addresses keep their positions; new ip_addresses append.\nMust be all specified or all omitted across all ip_addresses in the request." }, "subnet_id": { "type": "string", "format": "uuid" } } }, "IdentifiedRule_ContainerRule": { "type": "object", "description": "Generic wrapper that gives any rule type a stable UUID identity.", "required": [ "id", "rule" ], "properties": { "id": { "type": "string", "format": "uuid" }, "rule": { "oneOf": [ { "type": "string", "enum": [ "BySubnet" ] }, { "type": "string", "enum": [ "MergeDockerBridges" ] }, { "type": "object", "required": [ "ByApplication" ], "properties": { "ByApplication": { "type": "object", "properties": { "tag_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } } } }, { "type": "string", "enum": [ "ByHost" ] } ], "description": "Rules that change which containers exist and how they nest.\nContainer titles are data-driven (subnet CIDR, host names), not user-configurable." } } }, "IdentifiedRule_ElementRule": { "type": "object", "description": "Generic wrapper that gives any rule type a stable UUID identity.", "required": [ "id", "rule" ], "properties": { "id": { "type": "string", "format": "uuid" }, "rule": { "oneOf": [ { "type": "object", "required": [ "ByServiceCategory" ], "properties": { "ByServiceCategory": { "type": "object", "required": [ "categories" ], "properties": { "categories": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceCategory" } }, "is_infra_rule": { "type": "boolean", "description": "Set by the backend on the default infrastructure rule.\nFrontend uses this to identify the infra container for auto-collapse.", "readOnly": true }, "title": { "type": [ "string", "null" ] } } } } }, { "type": "object", "required": [ "ByTag" ], "properties": { "ByTag": { "type": "object", "required": [ "tag_ids" ], "properties": { "tag_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "title": { "type": [ "string", "null" ] } } } } }, { "type": "string", "enum": [ "ByHypervisor" ] }, { "type": "string", "enum": [ "ByContainerRuntime" ] }, { "type": "string", "enum": [ "ByStack" ] }, { "type": "string", "description": "Groups trunk ports (ports with tagged VLANs) into a \"Trunk Ports\" subcontainer.\nHigher priority than ByVLAN — prevents trunk ports from being grouped by VLAN.", "enum": [ "ByTrunkPort" ] }, { "type": "string", "description": "Groups access ports by their native VLAN ID into per-VLAN subcontainers.", "enum": [ "ByVLAN" ] }, { "type": "string", "description": "Groups ports by operational status (Up, Down, etc.) into per-status subcontainers.", "enum": [ "ByPortOpStatus" ] } ], "description": "Rules that organize nodes within a container into sub-groups." } } }, "IfAdminStatus": { "type": "string", "description": "SNMP ifAdminStatus values per IF-MIB RFC 2863", "enum": [ "Up", "Down", "Testing" ] }, "IfOperStatus": { "type": "string", "description": "SNMP ifOperStatus values per IF-MIB RFC 2863", "enum": [ "Up", "Down", "Testing", "Unknown", "Dormant", "NotPresent", "LowerLayerDown" ] }, "InlineGroup": { "type": "object", "description": "Visual grouping metadata for inlined entities.\nEntities sharing the same `group_id` are rendered together in the element card.", "required": [ "entity_id", "group_id", "role" ], "properties": { "entity_id": { "type": "string", "format": "uuid", "description": "The inlined entity's ID (e.g., service ID)." }, "group_id": { "type": "string", "format": "uuid", "description": "Shared by all members of the visual group." }, "role": { "$ref": "#/components/schemas/InlineGroupRole" } } }, "InlineGroupRole": { "type": "string", "description": "Role of an inlined entity within its visual group.", "enum": [ "Header", "Member" ] }, "Interface": { "allOf": [ { "$ref": "#/components/schemas/InterfaceBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "InterfaceBase": { "type": "object", "required": [ "host_id", "network_id", "if_index", "if_descr", "if_type", "admin_status", "oper_status" ], "properties": { "admin_status": { "$ref": "#/components/schemas/IfAdminStatus", "description": "SNMP ifAdminStatus: 1=up, 2=down, 3=testing" }, "cdp_address": { "type": [ "string", "null" ], "description": "Remote management IP from CDP (cdpCacheAddress)" }, "cdp_device_id": { "type": [ "string", "null" ], "description": "Remote device ID from CDP (typically hostname, locally unique)" }, "cdp_platform": { "type": [ "string", "null" ], "description": "Remote platform from CDP (e.g., \"Cisco IOS\")" }, "cdp_port_id": { "type": [ "string", "null" ], "description": "Remote port ID from CDP" }, "fdb_macs": { "type": [ "array", "null" ], "items": { "type": "string" }, "description": "Bridge FDB: learned MAC addresses on this switch port.\nSingle-MAC ports can be resolved to neighbor links server-side.\nMulti-MAC ports indicate uplinks where LLDP/CDP is the better source." }, "host_id": { "type": "string", "format": "uuid" }, "if_alias": { "type": [ "string", "null" ], "description": "SNMP ifAlias - user-configured description" }, "if_descr": { "type": "string", "description": "SNMP ifDescr - interface description (e.g., GigabitEthernet0/1)" }, "if_index": { "type": "integer", "format": "int32", "description": "SNMP ifIndex - stable identifier within device" }, "if_name": { "type": [ "string", "null" ], "description": "SNMP ifName - short interface name (e.g., Gi1/0/1)" }, "if_type": { "type": "integer", "format": "int32", "description": "SNMP ifType - IANAifType integer (6=ethernet, 24=loopback, etc.)" }, "ip_address_id": { "type": [ "string", "null" ], "format": "uuid", "description": "FK to IPAddress entity - this port's IP assignment (must be on same host).\nOld daemons send this as \"interface_id\"." }, "lldp_chassis_id": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/LldpChassisId", "description": "Remote chassis identifier from LLDP neighbor (globally/locally unique)" } ] }, "lldp_mgmt_addr": { "type": [ "string", "null" ], "description": "Remote management IP from LLDP neighbor (lldpRemManAddr)" }, "lldp_port_desc": { "type": [ "string", "null" ], "description": "Remote port description from LLDP neighbor (lldpRemPortDesc)" }, "lldp_port_id": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/LldpPortId", "description": "Remote port identifier from LLDP neighbor" } ] }, "lldp_sys_desc": { "type": [ "string", "null" ], "description": "Remote system description from LLDP neighbor (lldpRemSysDesc) - platform info" }, "lldp_sys_name": { "type": [ "string", "null" ], "description": "Remote system name from LLDP neighbor (lldpRemSysName)" }, "mac_address": { "type": [ "string", "null" ], "description": "MAC address from SNMP ifPhysAddress - immutable once set" }, "native_vlan_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Native/untagged VLAN entity ID on this port (resolved from Q-BRIDGE dot1qPvid)" }, "neighbor": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/Neighbor", "description": "Resolved neighbor connection (mutually exclusive: either Interface or Host)" } ] }, "network_id": { "type": "string", "format": "uuid" }, "oper_status": { "$ref": "#/components/schemas/IfOperStatus", "description": "SNMP ifOperStatus: 1=up, 2=down, 3=testing, 4=unknown, 5=dormant, 6=notPresent, 7=lowerLayerDown" }, "speed_bps": { "type": [ "integer", "null" ], "format": "int64", "description": "Interface speed from ifSpeed/ifHighSpeed in bits per second" }, "vlan_ids": { "type": [ "array", "null" ], "items": { "type": "string", "format": "uuid" }, "description": "Tagged VLAN entity IDs on this port (resolved from Q-BRIDGE dot1qVlanCurrentEgressPorts)" } } }, "InterfaceInput": { "type": "object", "description": "Input for creating an SNMP interface entry (ifTable data).\nUsed in CreateHostRequest. Server assigns UUIDs since nothing references\nInterface IDs at creation time (neighbor resolution is done server-side).", "required": [ "if_index", "if_descr" ], "properties": { "admin_status": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/IfAdminStatus", "description": "SNMP ifAdminStatus" } ] }, "if_alias": { "type": [ "string", "null" ], "description": "SNMP ifAlias - user-configured description" }, "if_descr": { "type": "string", "description": "SNMP ifDescr - interface description (e.g., GigabitEthernet0/1)" }, "if_index": { "type": "integer", "format": "int32", "description": "SNMP ifIndex - stable identifier within device" }, "if_type": { "type": [ "integer", "null" ], "format": "int32", "description": "SNMP ifType - IANAifType integer (6=ethernet, 24=loopback, etc.)" }, "ip_address_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Optional FK to Interface - links this SNMP port to its IP assignment" }, "mac_address": { "type": [ "string", "null" ], "description": "MAC address from SNMP ifPhysAddress" }, "oper_status": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/IfOperStatus", "description": "SNMP ifOperStatus" } ] }, "speed_bps": { "type": [ "integer", "null" ], "format": "int64", "description": "Interface speed in bits per second" } } }, "Invite": { "allOf": [ { "$ref": "#/components/schemas/InviteBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "InviteBase": { "type": "object", "required": [ "organization_id", "permissions", "network_ids", "url", "created_by", "expires_at", "send_to" ], "properties": { "created_by": { "type": "string", "format": "uuid" }, "expires_at": { "type": "string", "format": "date-time" }, "network_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "organization_id": { "type": "string", "format": "uuid" }, "permissions": { "$ref": "#/components/schemas/UserOrgPermissions" }, "send_to": { "type": [ "string", "null" ], "description": "Optional email address to send the invite to" }, "url": { "type": "string" } } }, "Ixy": { "type": "object", "required": [ "x", "y" ], "properties": { "x": { "type": "integer" }, "y": { "type": "integer" } } }, "LldpChassisId": { "oneOf": [ { "type": "object", "description": "Subtype 1: Chassis component (e.g., backplane serial number)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "ChassisComponent" ] }, "value": { "type": "string", "description": "Subtype 1: Chassis component (e.g., backplane serial number)" } } }, { "type": "object", "description": "Subtype 2: Interface alias (ifAlias from IF-MIB)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "InterfaceAlias" ] }, "value": { "type": "string", "description": "Subtype 2: Interface alias (ifAlias from IF-MIB)" } } }, { "type": "object", "description": "Subtype 3: Port component (e.g., backplane port number)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "PortComponent" ] }, "value": { "type": "string", "description": "Subtype 3: Port component (e.g., backplane port number)" } } }, { "type": "object", "description": "Subtype 4: MAC address (most common)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "MacAddress" ] }, "value": { "type": "string", "description": "Subtype 4: MAC address (most common)" } } }, { "type": "object", "description": "Subtype 5: Network address (IP address stored as string)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "NetworkAddress" ] }, "value": { "type": "string", "description": "Subtype 5: Network address (IP address stored as string)" } } }, { "type": "object", "description": "Subtype 6: Interface name (ifName from IF-MIB)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "InterfaceName" ] }, "value": { "type": "string", "description": "Subtype 6: Interface name (ifName from IF-MIB)" } } }, { "type": "object", "description": "Subtype 7: Locally assigned (device-specific identifier)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "LocallyAssigned" ] }, "value": { "type": "string", "description": "Subtype 7: Locally assigned (device-specific identifier)" } } } ], "description": "LLDP Chassis ID subtypes per IEEE 802.1AB.\n\nThe chassis ID identifies the remote device. Different network equipment\nmay use different subtypes depending on configuration and capabilities." }, "LldpPortId": { "oneOf": [ { "type": "object", "description": "Subtype 1: Interface alias (ifAlias from IF-MIB)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "InterfaceAlias" ] }, "value": { "type": "string", "description": "Subtype 1: Interface alias (ifAlias from IF-MIB)" } } }, { "type": "object", "description": "Subtype 2: Port component (e.g., backplane port number)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "PortComponent" ] }, "value": { "type": "string", "description": "Subtype 2: Port component (e.g., backplane port number)" } } }, { "type": "object", "description": "Subtype 3: MAC address", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "MacAddress" ] }, "value": { "type": "string", "description": "Subtype 3: MAC address" } } }, { "type": "object", "description": "Subtype 4: Network address (IP address stored as string)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "NetworkAddress" ] }, "value": { "type": "string", "description": "Subtype 4: Network address (IP address stored as string)" } } }, { "type": "object", "description": "Subtype 5: Interface name (ifName from IF-MIB)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "InterfaceName" ] }, "value": { "type": "string", "description": "Subtype 5: Interface name (ifName from IF-MIB)" } } }, { "type": "object", "description": "Subtype 6: Agent circuit ID (used by some providers)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "AgentCircuitId" ] }, "value": { "type": "string", "description": "Subtype 6: Agent circuit ID (used by some providers)" } } }, { "type": "object", "description": "Subtype 7: Locally assigned (device-specific identifier)", "required": [ "value", "subtype" ], "properties": { "subtype": { "type": "string", "enum": [ "LocallyAssigned" ] }, "value": { "type": "string", "description": "Subtype 7: Locally assigned (device-specific identifier)" } } } ], "description": "LLDP Port ID subtypes per IEEE 802.1AB.\n\nThe port ID identifies the specific port on the remote device." }, "LoginRequest": { "type": "object", "description": "Login request from client", "required": [ "email", "password" ], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" } } }, "MatchConfidence": { "type": "string", "enum": [ "NotApplicable", "Low", "Medium", "High", "Certain" ] }, "MatchDetails": { "type": "object", "required": [ "reason", "confidence" ], "properties": { "confidence": { "$ref": "#/components/schemas/MatchConfidence" }, "reason": { "$ref": "#/components/schemas/MatchReason" } } }, "MatchReason": { "oneOf": [ { "type": "object", "required": [ "type", "data" ], "properties": { "data": { "type": "string" }, "type": { "type": "string", "enum": [ "reason" ] } } }, { "type": "object", "required": [ "type", "data" ], "properties": { "data": { "type": "array", "items": {}, "description": "Tuple of [name: string, children: MatchReason[]]" }, "type": { "type": "string", "enum": [ "container" ] } } } ], "description": "Match reason - either a simple reason string or a container with nested reasons" }, "Neighbor": { "oneOf": [ { "type": "object", "description": "Full resolution - the specific remote port was identified", "required": [ "id", "type" ], "properties": { "id": { "type": "string", "format": "uuid", "description": "Full resolution - the specific remote port was identified" }, "type": { "type": "string", "enum": [ "Interface" ] } } }, { "type": "object", "description": "Partial resolution - the remote device was identified but not the specific port", "required": [ "id", "type" ], "properties": { "id": { "type": "string", "format": "uuid", "description": "Partial resolution - the remote device was identified but not the specific port" }, "type": { "type": "string", "enum": [ "Host" ] } } } ], "description": "Resolved LLDP/CDP neighbor connection.\n\nRepresents the remote endpoint this port connects to, discovered via LLDP or CDP.\nThe two variants are mutually exclusive and represent different resolution states." }, "Network": { "allOf": [ { "$ref": "#/components/schemas/NetworkBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "created_at": "2026-01-15T10:30:00Z", "credential_ids": [], "id": "550e8400-e29b-41d4-a716-446655440002", "name": "Home Network", "organization_id": "550e8400-e29b-41d4-a716-446655440001", "tags": [], "updated_at": "2026-01-15T10:30:00Z" } }, "NetworkBase": { "type": "object", "required": [ "name", "organization_id", "tags", "credential_ids" ], "properties": { "credential_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Credential IDs associated with this network (hydrated from junction table)." }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "NetworkSetup": { "type": "object", "description": "Network configuration for setup", "required": [ "name" ], "properties": { "name": { "type": "string" }, "snmp_community": { "type": [ "string", "null" ], "description": "SNMP community string (for V2c)" }, "snmp_enabled": { "type": "boolean", "description": "Whether SNMP is enabled for this network" }, "snmp_version": { "type": [ "string", "null" ], "description": "SNMP version (\"V2c\" or \"V3\")" } } }, "NetworkSummary": { "type": "object", "description": "Per-network summary of entity counts", "required": [ "id", "name", "host_count", "service_count", "subnet_count", "daemon_count" ], "properties": { "daemon_count": { "type": "integer", "format": "int64", "minimum": 0 }, "host_count": { "type": "integer", "format": "int64", "minimum": 0 }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "service_count": { "type": "integer", "format": "int64", "minimum": 0 }, "subnet_count": { "type": "integer", "format": "int64", "minimum": 0 } } }, "Node": { "allOf": [ { "$ref": "#/components/schemas/NodeType" }, { "type": "object", "required": [ "id", "position", "size" ], "properties": { "header": { "type": [ "string", "null" ] }, "id": { "type": "string", "format": "uuid" }, "position": { "$ref": "#/components/schemas/Ixy" }, "size": { "$ref": "#/components/schemas/Uxy" } } } ] }, "NodeType": { "oneOf": [ { "type": "object", "required": [ "node_type" ], "properties": { "associated_service_definition": { "type": [ "string", "null" ], "description": "Service definition ID for logo rendering (e.g. \"Docker\", \"Proxmox VE\").\nUsed by Hypervisor and Stack subcontainers to show the service's logo." }, "color": { "type": [ "string", "null" ], "description": "Display color name (set by graph builder from the source entity, e.g. subnet type)" }, "container_type": { "$ref": "#/components/schemas/ContainerType" }, "element_rule_id": { "type": [ "string", "null" ], "format": "uuid", "description": "ID of the element rule that created this container (for subcontainers like NestedTag, Hypervisor, etc.)" }, "entity_id": { "type": [ "string", "null" ], "format": "uuid", "description": "The entity this container represents (e.g. host ID for Host containers,\nsubnet ID for Subnet containers). Used for ownership mapping on the frontend." }, "icon": { "type": [ "string", "null" ], "description": "Display icon name (set by graph builder from the source entity, e.g. subnet type)" }, "node_type": { "type": "string", "enum": [ "Container" ] }, "parent_container_id": { "type": [ "string", "null" ], "format": "uuid" }, "will_accept_edges": { "type": "boolean", "description": "When true, this container accepts edges with `will_target_container`, causing\nthem to visually attach here instead of at elements inside." } } }, { "allOf": [ { "$ref": "#/components/schemas/ElementEntityType" }, { "type": "object", "required": [ "host_id" ], "properties": { "container_id": { "type": "string", "format": "uuid" }, "host_id": { "type": "string", "format": "uuid" }, "inline_groups": { "type": "array", "items": { "$ref": "#/components/schemas/InlineGroup" }, "description": "Visual grouping metadata for services inlined on this element.\nPopulated by element rules (e.g., Docker containers on a VM host\nget InlineGroups with Header/Member roles for dotted-border rendering)." } } }, { "type": "object", "required": [ "node_type" ], "properties": { "node_type": { "type": "string", "enum": [ "Element" ] } } } ] } ] }, "OidcProviderMetadata": { "type": "object", "required": [ "name", "slug" ], "properties": { "logo": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "slug": { "type": "string" } } }, "OnboardingNetworkState": { "type": "object", "description": "Network data in onboarding state response", "required": [ "name" ], "properties": { "id": { "type": [ "string", "null" ], "format": "uuid", "description": "Network ID (if created)" }, "name": { "type": "string", "description": "Network name" }, "snmp_community": { "type": [ "string", "null" ], "description": "SNMP community string" }, "snmp_enabled": { "type": "boolean", "description": "Whether SNMP is enabled" }, "snmp_version": { "type": [ "string", "null" ], "description": "SNMP version" } } }, "OnboardingOperation": { "type": "string", "enum": [ "OrgCreated", "OnboardingModalCompleted", "PlanSelected", "FirstDaemonRegistered", "FirstTopologyRebuild", "FirstDiscoveryCompleted", "FirstHostDiscovered", "SecondNetworkCreated", "FirstTagCreated", "FirstDependencyCreated", "FirstUserApiKeyCreated", "FirstSnmpCredentialCreated", "FirstApplicationTagCreated", "FirstCredentialCreated", "InviteSent", "InviteAccepted", "ProfileCompleted", "ReferralSourceCompleted" ] }, "OnboardingStateResponse": { "type": "object", "description": "Response from onboarding state endpoint", "properties": { "network": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/OnboardingNetworkState", "description": "Network from pending setup (with name and ID)" } ] }, "network_id": { "type": [ "string", "null" ], "format": "uuid", "description": "Network ID from pending setup (if any)" }, "org_name": { "type": [ "string", "null" ], "description": "Organization name from pending setup" }, "step": { "type": [ "string", "null" ], "description": "Current onboarding step (if any)" }, "use_case": { "type": [ "string", "null" ], "description": "Use case selection (homelab, company, msp)" } } }, "OnboardingStepRequest": { "type": "object", "description": "Request to save onboarding step", "required": [ "step" ], "properties": { "referral_source": { "type": [ "string", "null" ], "description": "Referral source (how they heard about Scanopy)" }, "referral_source_other": { "type": [ "string", "null" ], "description": "Free-text referral source (when \"other\" is selected)" }, "step": { "type": "string" }, "use_case": { "type": [ "string", "null" ], "description": "Use case selection (homelab, company, msp)" } } }, "OrderDirection": { "type": "string", "description": "Direction for ORDER BY clauses.", "enum": [ "asc", "desc" ] }, "Organization": { "allOf": [ { "$ref": "#/components/schemas/OrganizationBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "OrganizationBase": { "type": "object", "required": [ "name", "plan", "plan_status", "onboarding" ], "properties": { "has_payment_method": { "type": "boolean", "readOnly": true }, "name": { "type": "string" }, "onboarding": { "type": "array", "items": { "$ref": "#/components/schemas/OnboardingOperation" } }, "plan": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/BillingPlan" } ] }, "plan_status": { "type": [ "string", "null" ], "readOnly": true }, "trial_end_date": { "type": [ "string", "null" ], "format": "date-time", "readOnly": true }, "use_case": { "$ref": "#/components/schemas/UseCase", "description": "Use case selection (homelab, company, msp, other)" } } }, "PaginatedApiMeta": { "type": "object", "description": "API metadata for paginated list responses (pagination is always present)", "required": [ "api_version", "server_version", "pagination" ], "properties": { "api_version": { "type": "integer", "format": "int32", "description": "API version (integer, increments on breaking changes)", "minimum": 0 }, "pagination": { "$ref": "#/components/schemas/PaginationMeta", "description": "Pagination info" }, "server_version": { "type": "string", "description": "Server version (semver)", "example": "0.16.0" } }, "example": { "api_version": 1, "pagination": { "has_more": true, "limit": 50, "offset": 0, "total_count": 142 }, "server_version": "0.16.0" } }, "PaginatedApiResponse_Credential": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/CredentialBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_DaemonResponse": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/DaemonBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at", "version_status" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "version_status": { "$ref": "#/components/schemas/DaemonVersionStatus", "description": "Computed version status including health and warnings" } } } ], "description": "Daemon response for UI including computed version status" } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_Dependency": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/DependencyBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "color": "Blue", "created_at": "2026-01-15T10:30:00Z", "dependency_type": "RequestPath", "description": "HTTP/HTTPS services dependency", "edge_style": "Bezier", "id": "550e8400-e29b-41d4-a716-446655440008", "members": { "service_ids": [], "type": "Services" }, "name": "Web Services", "network_id": "550e8400-e29b-41d4-a716-446655440002", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_HostResponse": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "type": "object", "description": "Response type for host endpoints.\nIncludes children (ip_addresses, ports, services, interfaces).", "required": [ "id", "created_at", "updated_at", "name", "network_id", "source", "hidden", "tags", "ip_addresses", "ports", "services", "interfaces" ], "properties": { "chassis_id": { "type": [ "string", "null" ] }, "created_at": { "type": "string", "format": "date-time" }, "credential_assignments": { "type": "array", "items": { "$ref": "#/components/schemas/CredentialAssignment" } }, "description": { "type": [ "string", "null" ] }, "hidden": { "type": "boolean" }, "hostname": { "type": [ "string", "null" ] }, "id": { "type": "string", "format": "uuid" }, "interfaces": { "type": "array", "items": { "$ref": "#/components/schemas/Interface" }, "description": "SNMP ifTable entries" }, "ip_addresses": { "type": "array", "items": { "$ref": "#/components/schemas/IPAddress" } }, "management_url": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "ports": { "type": "array", "items": { "$ref": "#/components/schemas/Port" } }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/Service" } }, "source": { "$ref": "#/components/schemas/EntitySource" }, "sys_contact": { "type": [ "string", "null" ] }, "sys_descr": { "type": [ "string", "null" ] }, "sys_location": { "type": [ "string", "null" ] }, "sys_object_id": { "type": [ "string", "null" ] }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "updated_at": { "type": "string", "format": "date-time" }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostVirtualization" } ] } }, "example": { "created_at": "2026-01-15T10:30:00Z", "credential_assignments": [], "description": "Primary web server", "hidden": false, "hostname": "web-server-01.local", "id": "550e8400-e29b-41d4-a716-446655440003", "interfaces": [ { "admin_status": "Up", "cdp_address": null, "cdp_device_id": null, "cdp_platform": null, "cdp_port_id": null, "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-44665544000f", "if_alias": "Uplink to Core Switch", "if_descr": "GigabitEthernet0/1", "if_index": 1, "if_name": "Gi0/1", "if_type": 6, "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "lldp_chassis_id": null, "lldp_mgmt_addr": null, "lldp_port_desc": null, "lldp_port_id": null, "lldp_sys_desc": null, "lldp_sys_name": null, "mac_address": "DE:AD:BE:EF:CA:FE", "neighbor": null, "network_id": "550e8400-e29b-41d4-a716-446655440002", "oper_status": "Up", "speed_bps": 1000000000, "updated_at": "2026-01-15T10:30:00Z" } ], "ip_addresses": [ { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440005", "ip_address": "192.168.1.100", "mac_address": "DE:AD:BE:EF:CA:FE", "name": "eth0", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "subnet_id": "550e8400-e29b-41d4-a716-446655440004", "updated_at": "2026-01-15T10:30:00Z" } ], "name": "web-server-01", "network_id": "550e8400-e29b-41d4-a716-446655440002", "ports": [ { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440006", "network_id": "550e8400-e29b-41d4-a716-446655440002", "number": 80, "protocol": "Tcp", "type": "Http", "updated_at": "2026-01-15T10:30:00Z" } ], "services": [ { "bindings": [ { "created_at": "2026-04-18T15:49:08.286655Z", "id": "41b2b45b-a45d-4e9e-8975-4420c8e136ef", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.286655Z" } ], "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440007", "name": "nginx", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "service_definition": "Zabbix Agent", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } ], "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_Service": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/ServiceBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "bindings": [ { "created_at": "2026-04-18T15:49:08.315205Z", "id": "c2403f4a-41bf-4332-9531-70af5b621dea", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.315205Z" } ], "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440007", "name": "nginx", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "service_definition": "Zabbix Agent", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_Subnet": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/SubnetBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "cidr": "192.168.1.0/24", "created_at": "2026-01-15T10:30:00Z", "description": "Local area network", "id": "550e8400-e29b-41d4-a716-446655440004", "name": "LAN", "network_id": "550e8400-e29b-41d4-a716-446655440002", "source": { "type": "Manual" }, "subnet_type": "Lan", "tags": [], "updated_at": "2026-01-15T10:30:00Z" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_Tag": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/TagBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "color": "Green", "created_at": "2026-01-15T10:30:00Z", "description": "Production environment resources", "id": "550e8400-e29b-41d4-a716-44665544000a", "is_application": false, "name": "production", "organization_id": "550e8400-e29b-41d4-a716-446655440001", "updated_at": "2026-01-15T10:30:00Z" } } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_Topology": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/TopologyBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_User": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/UserBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_UserApiKey": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/UserApiKeyBase" }, { "type": "object", "required": [ "id", "updated_at", "created_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginatedApiResponse_Vlan": { "type": "object", "description": "Response type for paginated list endpoints (pagination is always present in meta)", "required": [ "success", "data", "meta" ], "properties": { "data": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/VlanBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] } }, "error": { "type": [ "string", "null" ] }, "meta": { "$ref": "#/components/schemas/PaginatedApiMeta" }, "success": { "type": "boolean" } } }, "PaginationMeta": { "type": "object", "description": "Pagination metadata returned with paginated responses.", "required": [ "total_count", "limit", "offset", "has_more" ], "properties": { "has_more": { "type": "boolean", "description": "Whether there are more items after this page" }, "limit": { "type": "integer", "format": "int32", "description": "Maximum items per page (as requested)", "minimum": 0 }, "offset": { "type": "integer", "format": "int32", "description": "Number of items skipped", "minimum": 0 }, "total_count": { "type": "integer", "format": "int64", "description": "Total number of items matching the filter (ignoring pagination)", "minimum": 0 } }, "example": { "has_more": true, "limit": 50, "offset": 0, "total_count": 142 } }, "PaginationParams": { "type": "object", "description": "Pagination parameters that can be composed into filter queries.\n\nDefault behavior:\n- `limit`: 50 (returns up to 50 results)\n- `offset`: 0 (starts from the beginning)\n- `limit=0`: No limit (returns all results)\n- `limit` values above 1000 are capped to 1000", "properties": { "limit": { "type": [ "integer", "null" ], "format": "int32", "description": "Maximum number of results to return (1-1000, default: 50). Use 0 for no limit.", "minimum": 0 }, "offset": { "type": [ "integer", "null" ], "format": "int32", "description": "Number of results to skip. Default: 0.", "minimum": 0 } } }, "PlanConfig": { "type": "object", "required": [ "base_cents", "rate", "trial_days" ], "properties": { "base_cents": { "type": "integer", "format": "int64" }, "host_cents": { "type": [ "integer", "null" ], "format": "int64" }, "included_hosts": { "type": [ "integer", "null" ], "format": "int64", "minimum": 0 }, "included_networks": { "type": [ "integer", "null" ], "format": "int64", "minimum": 0 }, "included_seats": { "type": [ "integer", "null" ], "format": "int64", "minimum": 0 }, "network_cents": { "type": [ "integer", "null" ], "format": "int64" }, "rate": { "$ref": "#/components/schemas/BillingRate" }, "seat_cents": { "type": [ "integer", "null" ], "format": "int64" }, "trial_days": { "type": "integer", "format": "int32", "minimum": 0 } } }, "PlanUsage": { "type": "object", "description": "Plan usage limits and current counts", "required": [ "host_count", "network_count", "seat_count" ], "properties": { "host_count": { "type": "integer", "format": "int64", "minimum": 0 }, "host_limit": { "type": [ "integer", "null" ], "format": "int64", "minimum": 0 }, "network_count": { "type": "integer", "format": "int64", "minimum": 0 }, "network_limit": { "type": [ "integer", "null" ], "format": "int64", "minimum": 0 }, "seat_count": { "type": "integer", "format": "int64", "minimum": 0 }, "seat_limit": { "type": [ "integer", "null" ], "format": "int64", "minimum": 0 } } }, "Port": { "allOf": [ { "$ref": "#/components/schemas/PortBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "description": "Port entity with custom serialization that flattens PortType fields.", "example": { "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440006", "network_id": "550e8400-e29b-41d4-a716-446655440002", "number": 80, "protocol": "Tcp", "type": "Http", "updated_at": "2026-01-15T10:30:00Z" } }, "PortBase": { "allOf": [ { "$ref": "#/components/schemas/PortType" }, { "type": "object", "required": [ "host_id", "network_id" ], "properties": { "host_id": { "type": "string", "format": "uuid" }, "network_id": { "type": "string", "format": "uuid" } } } ], "description": "The base data for a Port entity (everything except id, created_at, updated_at)" }, "PortInput": { "type": "object", "description": "Input for creating or updating a port.\nUsed in both CreateHostRequest and UpdateHostRequest.\nClient must provide a UUID for the port.", "required": [ "id", "number", "protocol" ], "properties": { "id": { "type": "string", "format": "uuid", "description": "Client-provided UUID for this port" }, "number": { "type": "integer", "format": "int32", "description": "Port number (1-65535)", "minimum": 0 }, "protocol": { "$ref": "#/components/schemas/TransportProtocol", "description": "Transport protocol (Tcp or Udp)" } } }, "PortType": { "type": "object", "description": "Port type with number, protocol, and optional type identifier", "required": [ "number", "protocol" ], "properties": { "number": { "type": "integer" }, "protocol": { "type": "string", "enum": [ "Udp", "Tcp" ] }, "type": { "type": "string", "description": "Auto-derived from number+protocol; optional on create" } } }, "ProfileUpdateRequest": { "type": "object", "description": "Request to update user profile (deferred marketing fields)", "properties": { "company_size": { "type": [ "string", "null" ] }, "job_title": { "type": [ "string", "null" ] } } }, "ProvisionDaemonRequest": { "type": "object", "description": "Request to pre-provision a ServerPoll mode daemon.\nThis creates the daemon record on the server before the daemon is installed.", "required": [ "name", "network_id", "url" ], "properties": { "name": { "type": "string", "description": "Human-readable name for the daemon." }, "network_id": { "type": "string", "format": "uuid", "description": "Network this daemon will be associated with." }, "url": { "type": "string", "description": "URL where the server can reach the daemon (required for ServerPoll mode)." } } }, "ProvisionDaemonResponse": { "type": "object", "description": "Response from provisioning a daemon.\nContains the daemon record and the API key (shown only once).", "required": [ "daemon", "daemon_api_key" ], "properties": { "daemon": { "$ref": "#/components/schemas/DaemonResponse", "description": "The created daemon record (with version status)." }, "daemon_api_key": { "type": "string", "description": "The API key (plaintext) for daemon authentication.\nThis is shown only once - store it securely." } } }, "ProxmoxVirtualization": { "type": "object", "required": [ "service_id" ], "properties": { "service_id": { "type": "string", "format": "uuid" }, "vm_id": { "type": [ "string", "null" ] }, "vm_name": { "type": [ "string", "null" ] } } }, "PublicConfigResponse": { "type": "object", "required": [ "server_port", "disable_registration", "disable_password_login", "oidc_providers", "billing_enabled", "has_integrated_daemon", "has_email_service", "has_email_opt_in", "public_url", "needs_cookie_consent", "deployment_type" ], "properties": { "billing_enabled": { "type": "boolean" }, "deployment_type": { "$ref": "#/components/schemas/DeploymentType" }, "disable_password_login": { "type": "boolean" }, "disable_registration": { "type": "boolean" }, "has_email_opt_in": { "type": "boolean" }, "has_email_service": { "type": "boolean" }, "has_integrated_daemon": { "type": "boolean" }, "license_expiry": { "type": [ "string", "null" ] }, "license_status": { "type": [ "string", "null" ] }, "needs_cookie_consent": { "type": "boolean" }, "oidc_providers": { "type": "array", "items": { "$ref": "#/components/schemas/OidcProviderMetadata" } }, "posthog_key": { "type": [ "string", "null" ] }, "public_url": { "type": "string" }, "server_port": { "type": "integer", "format": "int32", "minimum": 0 } } }, "PublicShareMetadata": { "type": "object", "description": "Public share metadata (returned without authentication)", "required": [ "id", "name", "requires_password", "options", "enabled_views" ], "properties": { "enabled_views": { "type": "array", "items": { "$ref": "#/components/schemas/TopologyView" }, "description": "Resolved list of available topology views for this share.\nFiltered by both share configuration and data availability.\nFirst element is the default view." }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "options": { "$ref": "#/components/schemas/ShareOptions" }, "requires_password": { "type": "boolean" } } }, "ReferralSourceRequest": { "type": "object", "description": "Request to submit referral source", "required": [ "referral_source" ], "properties": { "referral_source": { "type": "string" }, "referral_source_other": { "type": [ "string", "null" ] } } }, "RegisterRequest": { "type": "object", "description": "Registration request from client", "required": [ "email", "password", "terms_accepted" ], "properties": { "company_url": { "type": [ "string", "null" ], "description": "Honeypot field for bot detection" }, "email": { "type": "string", "format": "email" }, "marketing_opt_in": { "type": "boolean" }, "password": { "type": "string" }, "terms_accepted": { "type": "boolean" } } }, "RequestEmailChangeRequest": { "type": "object", "required": [ "new_email" ], "properties": { "current_password": { "type": [ "string", "null" ], "description": "Current password — required if the user already has a password set.\nNot required for OIDC-only users." }, "new_email": { "type": "string", "format": "email" } } }, "ResendVerificationRequest": { "type": "object", "description": "Request to resend verification email", "required": [ "email" ], "properties": { "email": { "type": "string", "format": "email" } } }, "ResetPasswordRequest": { "type": "object", "required": [ "token", "password" ], "properties": { "password": { "type": "string" }, "token": { "type": "string" } } }, "RunType": { "oneOf": [ { "type": "object", "title": "Scheduled", "required": [ "cron_schedule", "enabled", "type" ], "properties": { "cron_schedule": { "type": "string" }, "enabled": { "type": "boolean" }, "last_run": { "type": [ "string", "null" ], "format": "date-time", "readOnly": true }, "timezone": { "type": [ "string", "null" ], "description": "IANA timezone for cron evaluation, e.g. \"America/New_York\". None = UTC." }, "type": { "type": "string", "enum": [ "Scheduled" ] } } }, { "type": "object", "title": "Historical", "description": "Historical discovery runs are created by the server and cannot be submitted via API", "required": [ "results", "type" ], "properties": { "results": { "$ref": "#/components/schemas/DiscoveryUpdatePayload" }, "type": { "type": "string", "enum": [ "Historical" ] } } }, { "type": "object", "title": "AdHoc", "required": [ "type" ], "properties": { "last_run": { "type": [ "string", "null" ], "format": "date-time", "readOnly": true }, "type": { "type": "string", "enum": [ "AdHoc" ] } } } ] }, "ScanSettings": { "type": "object", "description": "Scan performance settings. Lives on the discovery entity.\nNumeric fields are `Option` — `None` means \"use daemon default\".\nThe daemon unwraps with defaults at point of use.", "properties": { "arp_rate_pps": { "type": [ "integer", "null" ], "format": "int32", "description": "ARP packets per second (default: 50)", "minimum": 0 }, "arp_retries": { "type": [ "integer", "null" ], "format": "int32", "description": "ARP retry rounds for non-responsive targets (default: 2 = 3 total attempts)", "minimum": 0 }, "arp_scan_cutoff": { "type": [ "integer", "null" ], "format": "int32", "description": "ARP scan cutoff prefix. Interfaced subnets larger than this prefix are\ntruncated to this many IPs. Default: 15 (= /15, ~131K IPs).\nLower values scan more IPs — increase arp_rate_pps accordingly.", "minimum": 0 }, "full_scan_interval": { "type": [ "integer", "null" ], "format": "int32", "description": "Run a full 65k port scan every N scans. Other scans use a light port set.\nDefault: 3. Value of 0 means never full scan. Value of 1 means every scan is full.", "minimum": 0 }, "is_full_scan": { "type": "boolean", "description": "Whether this specific scan run should do a full 65k port scan.\nSet by the server before dispatching to the daemon — not user-configurable." }, "port_scan_batch_size": { "type": [ "integer", "null" ], "description": "Ports scanned concurrently per host (default: 200, clamped 16-1000)", "minimum": 0 }, "probe_raw_socket_ports": { "type": "boolean", "description": "Whether to probe raw-socket ports 9100-9107 (default: false).\nDisabled by default to prevent ghost printing on JetDirect printers." }, "scan_rate_pps": { "type": [ "integer", "null" ], "format": "int32", "description": "Port scan probes per second (default: 500)", "minimum": 0 }, "use_npcap_arp": { "type": "boolean", "description": "On Windows, use Npcap broadcast ARP instead of SendARP (default: false)" } } }, "SecretValue": { "oneOf": [ { "type": "object", "required": [ "value", "mode" ], "properties": { "mode": { "type": "string", "enum": [ "Inline" ] }, "value": { "type": "string" } } }, { "type": "object", "required": [ "path", "mode" ], "properties": { "mode": { "type": "string", "enum": [ "FilePath" ] }, "path": { "type": "string" } } } ], "description": "Secret value that can be either inline content or a file path on the daemon host." }, "ServerCapabilities": { "type": "object", "description": "Server capabilities returned on startup/registration", "required": [ "server_version", "minimum_daemon_version" ], "properties": { "deprecation_warnings": { "type": "array", "items": { "$ref": "#/components/schemas/DeprecationWarning" }, "description": "Deprecation warnings for the daemon" }, "minimum_daemon_version": { "type": "string", "description": "Minimum daemon version supported by this server" }, "server_version": { "type": "string", "description": "Server software version" } } }, "Service": { "allOf": [ { "$ref": "#/components/schemas/ServiceBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "bindings": [ { "created_at": "2026-04-18T15:49:08.301658Z", "id": "8dc45508-23e6-4c42-9fb0-e3a9ba3f1353", "ip_address_id": "550e8400-e29b-41d4-a716-446655440005", "network_id": "550e8400-e29b-41d4-a716-446655440002", "port_id": "550e8400-e29b-41d4-a716-446655440006", "service_id": "550e8400-e29b-41d4-a716-446655440007", "type": "Port", "updated_at": "2026-04-18T15:49:08.301658Z" } ], "created_at": "2026-01-15T10:30:00Z", "host_id": "550e8400-e29b-41d4-a716-446655440003", "id": "550e8400-e29b-41d4-a716-446655440007", "name": "nginx", "network_id": "550e8400-e29b-41d4-a716-446655440002", "position": 0, "service_definition": "Zabbix Agent", "source": { "type": "Manual" }, "tags": [], "updated_at": "2026-01-15T10:30:00Z", "virtualization": null } }, "ServiceBase": { "type": "object", "required": [ "host_id", "network_id", "service_definition", "name", "bindings", "source", "tags", "position" ], "properties": { "bindings": { "type": "array", "items": { "$ref": "#/components/schemas/Binding" } }, "host_id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "position": { "type": "integer", "format": "int32", "description": "Position of this service in the host's service list (for ordering)" }, "service_definition": { "type": "string" }, "source": { "$ref": "#/components/schemas/EntitySource", "description": "Will be automatically set to Manual for creation through API" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServiceVirtualization" } ] } } }, "ServiceCategory": { "type": "string", "enum": [ "NetworkCore", "NetworkAccess", "NetworkAppliance", "RemoteAccess", "Storage", "Backup", "Media", "HomeAutomation", "Hypervisor", "ContainerRuntime", "Container", "Orchestrator", "DNS", "VPN", "Monitoring", "AdBlock", "ReverseProxy", "Workstation", "Mobile", "IoT", "Printer", "Database", "Development", "Dashboard", "MessageQueue", "IdentityAndAccess", "Office", "ProjectManagement", "Messaging", "Conferencing", "Telephony", "Email", "Publishing", "Unknown", "Custom", "Scanopy", "OpenPorts" ] }, "ServiceInput": { "type": "object", "description": "Input for creating or updating a service.\nUsed in both CreateHostRequest and UpdateHostRequest.\nClient must provide a UUID for the service.", "required": [ "id", "service_definition", "name" ], "properties": { "bindings": { "type": "array", "items": { "$ref": "#/components/schemas/BindingInput" }, "description": "Bindings that associate this service with ports/interfaces" }, "id": { "type": "string", "format": "uuid", "description": "Client-provided UUID for this service" }, "name": { "type": "string", "description": "Display name for this service" }, "position": { "type": [ "integer", "null" ], "format": "int32", "description": "Position in the host's service list (for ordering).\nIf omitted on create: appends to end of list.\nIf omitted on update: existing services keep their positions; new services append.\nMust be all specified or all omitted across all services in the request." }, "service_definition": { "type": "string", "description": "Service definition ID (e.g., \"Nginx\", \"PostgreSQL\")" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Tags for categorization" }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ServiceVirtualization", "description": "Container/VM virtualization info if applicable" } ] } } }, "ServiceOrderField": { "type": "string", "description": "Fields that services can be ordered/grouped by.", "enum": [ "created_at", "name", "updated_at", "host", "network_id", "position" ] }, "ServiceVirtualization": { "oneOf": [ { "type": "object", "title": "Docker", "required": [ "details", "type" ], "properties": { "details": { "$ref": "#/components/schemas/DockerVirtualization" }, "type": { "type": "string", "enum": [ "Docker" ] } } } ], "title": "ServiceVirtualization" }, "SetTagsRequest": { "type": "object", "description": "Request body for setting all tags on an entity", "required": [ "entity_type", "entity_id", "tag_ids" ], "properties": { "entity_id": { "type": "string", "format": "uuid", "description": "The entity ID" }, "entity_type": { "$ref": "#/components/schemas/EntityDiscriminants", "description": "The entity type (e.g., Host, Service, Subnet)" }, "tag_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "The new list of tag IDs" } } }, "SetupPaymentMethodRequest": { "type": "object", "required": [ "url" ], "properties": { "url": { "type": "string" } } }, "SetupRequest": { "type": "object", "description": "Setup request for pre-registration org/network configuration", "required": [ "organization_name", "network" ], "properties": { "network": { "$ref": "#/components/schemas/NetworkSetup" }, "organization_name": { "type": "string" } } }, "SetupResponse": { "type": "object", "description": "Response from setup endpoint", "required": [ "network_id" ], "properties": { "network_id": { "type": "string", "format": "uuid" } } }, "Share": { "allOf": [ { "$ref": "#/components/schemas/ShareBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "ShareAccessTokenResponse": { "type": "object", "description": "Access token returned after successful password verification.\n\nThe token is an HS256 JWT tied to the share's `password_hash` — changing\nthe share password implicitly invalidates all outstanding tokens.", "required": [ "access_token", "expires_at" ], "properties": { "access_token": { "type": "string" }, "expires_at": { "type": "string", "format": "date-time" } } }, "ShareBase": { "type": "object", "required": [ "topology_id", "network_id", "created_by", "name", "is_enabled", "expires_at", "allowed_domains", "options", "enabled_views" ], "properties": { "allowed_domains": { "type": [ "array", "null" ], "items": { "type": "string" } }, "created_by": { "type": "string", "format": "uuid" }, "enabled_views": { "type": [ "array", "null" ], "items": { "$ref": "#/components/schemas/TopologyView" }, "description": "Which topology views are enabled for this share.\nNone = all views (subject to data availability). Some(list) = only these views in order.\nFirst element is the default view shown on load." }, "expires_at": { "type": [ "string", "null" ], "format": "date-time" }, "is_enabled": { "type": "boolean" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "options": { "$ref": "#/components/schemas/ShareOptions" }, "topology_id": { "type": "string", "format": "uuid" } } }, "ShareOptions": { "type": "object", "description": "Share display options", "required": [ "show_inspect_panel", "show_zoom_controls", "show_export_button", "show_minimap" ], "properties": { "show_export_button": { "type": "boolean" }, "show_inspect_panel": { "type": "boolean" }, "show_minimap": { "type": "boolean" }, "show_zoom_controls": { "type": "boolean" } } }, "Subnet": { "allOf": [ { "$ref": "#/components/schemas/SubnetBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "cidr": "192.168.1.0/24", "created_at": "2026-01-15T10:30:00Z", "description": "Local area network", "id": "550e8400-e29b-41d4-a716-446655440004", "name": "LAN", "network_id": "550e8400-e29b-41d4-a716-446655440002", "source": { "type": "Manual" }, "subnet_type": "Lan", "tags": [], "updated_at": "2026-01-15T10:30:00Z" } }, "SubnetBase": { "type": "object", "required": [ "cidr", "network_id", "name", "subnet_type", "source", "tags" ], "properties": { "cidr": { "type": "string" }, "description": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "source": { "$ref": "#/components/schemas/EntitySource", "description": "Will be automatically set to Manual for creation through API" }, "subnet_type": { "$ref": "#/components/schemas/SubnetType" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/SubnetVirtualization", "description": "Virtualization provider that owns this subnet.\nDocker bridge subnets use this for per-host dedup (same CIDR on different hosts = distinct)." } ] } } }, "SubnetOrderField": { "type": "string", "description": "Fields that subnets can be ordered/grouped by.", "enum": [ "created_at", "name", "cidr", "subnet_type", "updated_at", "network_id" ] }, "SubnetType": { "type": "string", "enum": [ "Internet", "Remote", "Gateway", "VpnTunnel", "Dmz", "Lan", "WiFi", "IoT", "Guest", "DockerBridge", "MacVlan", "IpVlan", "Management", "Storage", "Loopback", "Unknown" ] }, "SubnetVirtualization": { "oneOf": [ { "allOf": [ { "$ref": "#/components/schemas/DockerSubnetVirtualization", "description": "Docker bridge network — host-scoped, same CIDR on different hosts are distinct subnets." }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "Docker" ] } } } ], "title": "Docker", "description": "Docker bridge network — host-scoped, same CIDR on different hosts are distinct subnets." } ], "description": "Virtualization metadata for subnets that belong to a virtual infrastructure.\nConsistent with HostVirtualization and ServiceVirtualization patterns.\nPoints to the service that provides the virtualization (e.g., Docker daemon)." }, "Tag": { "allOf": [ { "$ref": "#/components/schemas/TagBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ], "example": { "color": "Green", "created_at": "2026-01-15T10:30:00Z", "description": "Production environment resources", "id": "550e8400-e29b-41d4-a716-44665544000a", "is_application": false, "name": "production", "organization_id": "550e8400-e29b-41d4-a716-446655440001", "updated_at": "2026-01-15T10:30:00Z" } }, "TagBase": { "type": "object", "required": [ "name", "color", "organization_id" ], "properties": { "color": { "$ref": "#/components/schemas/Color" }, "description": { "type": [ "string", "null" ] }, "is_application": { "type": "boolean" }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" } } }, "TagOrderField": { "type": "string", "description": "Fields that tags can be ordered/grouped by.", "enum": [ "created_at", "name", "color", "updated_at", "is_application" ] }, "TestReachabilityRequest": { "type": "object", "description": "Request to test reachability of a daemon URL.", "required": [ "url" ], "properties": { "check_health": { "type": "boolean", "description": "If true, also perform an HTTP GET to {url}/health after the TCP check" }, "url": { "type": "string", "description": "Full URL of the daemon (e.g. \"https://daemon.example.com:60073\")" } } }, "TestReachabilityResponse": { "type": "object", "description": "Response from a reachability test.", "required": [ "reachable" ], "properties": { "error": { "type": [ "string", "null" ], "description": "Error message if not reachable" }, "health": { "type": [ "boolean", "null" ], "description": "Health check result (only present when check_health was true)" }, "reachable": { "type": "boolean", "description": "Whether the TCP connection succeeded" } } }, "Topology": { "allOf": [ { "$ref": "#/components/schemas/TopologyBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "TopologyBase": { "type": "object", "required": [ "name", "options", "network_id", "tags", "nodes", "edges", "hosts", "ip_addresses", "ports", "bindings", "subnets", "services", "dependencies", "interfaces", "entity_tags", "is_stale", "last_refreshed", "is_locked", "removed_hosts", "removed_ip_addresses", "removed_subnets", "removed_services", "removed_dependencies", "removed_ports", "removed_bindings", "removed_interfaces" ], "properties": { "bindings": { "type": "array", "items": { "$ref": "#/components/schemas/Binding" } }, "dependencies": { "type": "array", "items": { "$ref": "#/components/schemas/Dependency" } }, "edges": { "type": "array", "items": { "$ref": "#/components/schemas/Edge" } }, "entity_tags": { "type": "array", "items": { "$ref": "#/components/schemas/Tag" } }, "hosts": { "type": "array", "items": { "$ref": "#/components/schemas/Host" } }, "interfaces": { "type": "array", "items": { "$ref": "#/components/schemas/Interface" } }, "ip_addresses": { "type": "array", "items": { "$ref": "#/components/schemas/IPAddress" } }, "is_locked": { "type": "boolean" }, "is_stale": { "type": "boolean" }, "last_refreshed": { "type": "string", "format": "date-time" }, "locked_at": { "type": [ "string", "null" ], "format": "date-time" }, "locked_by": { "type": [ "string", "null" ], "format": "uuid" }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "nodes": { "type": "array", "items": { "$ref": "#/components/schemas/Node" } }, "options": { "$ref": "#/components/schemas/TopologyOptions" }, "parent_id": { "type": [ "string", "null" ], "format": "uuid" }, "ports": { "type": "array", "items": { "$ref": "#/components/schemas/Port" } }, "removed_bindings": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "removed_dependencies": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "removed_hosts": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "removed_interfaces": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "removed_ip_addresses": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "removed_ports": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "removed_services": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "removed_subnets": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "services": { "type": "array", "items": { "$ref": "#/components/schemas/Service" } }, "subnets": { "type": "array", "items": { "$ref": "#/components/schemas/Subnet" } }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "vlans": { "type": "array", "items": { "$ref": "#/components/schemas/Vlan" } } } }, "TopologyEdgeHandleUpdate": { "type": "object", "description": "Lightweight request type for updating an edge's handles.\n\nUsed for edge reconnect operations - instead of sending the entire topology,\nonly sends the edge ID and new handle positions.\nFixes HTTP 413 errors on edge reconnect operations.", "required": [ "network_id", "edge_id", "source_handle", "target_handle" ], "properties": { "edge_id": { "type": "string", "format": "uuid", "description": "ID of the edge to update" }, "network_id": { "type": "string", "format": "uuid", "description": "Network ID for authorization" }, "source_handle": { "$ref": "#/components/schemas/EdgeHandle", "description": "New source handle position" }, "target_handle": { "$ref": "#/components/schemas/EdgeHandle", "description": "New target handle position" } } }, "TopologyLocalOptions": { "type": "object", "required": [ "no_fade_edges", "hide_edge_types" ], "properties": { "bundle_edges": { "type": "boolean" }, "hide_edge_types": { "type": "array", "items": { "$ref": "#/components/schemas/EdgeTypeDiscriminants" } }, "no_fade_edges": { "type": "boolean" }, "show_minimap": { "type": "boolean" }, "tag_filter": { "$ref": "#/components/schemas/TopologyTagFilter" } } }, "TopologyMetadataUpdate": { "type": "object", "description": "Lightweight request type for updating topology metadata.\n\nUsed for editing topology name/parent - instead of sending the entire topology\n(which includes all hosts, ip_addresses, services, etc.), only sends the metadata fields.\nFixes HTTP 413 errors on metadata edit operations.", "required": [ "network_id", "name" ], "properties": { "name": { "type": "string", "description": "New name for the topology" }, "network_id": { "type": "string", "format": "uuid", "description": "Network ID for authorization" }, "parent_id": { "type": [ "string", "null" ], "format": "uuid", "description": "New parent topology ID (optional)" } } }, "TopologyNodePositionUpdate": { "type": "object", "description": "Lightweight request type for updating a single node's position.\n\nUsed for drag operations - instead of sending the entire topology (which can be\nseveral megabytes for large networks), only sends the node ID and new position.\nFixes HTTP 413 errors on drag operations.", "required": [ "network_id", "node_id", "position" ], "properties": { "network_id": { "type": "string", "format": "uuid", "description": "Network ID for authorization" }, "node_id": { "type": "string", "format": "uuid", "description": "ID of the node to update" }, "position": { "$ref": "#/components/schemas/Ixy", "description": "New position for the node" } } }, "TopologyNodeResizeUpdate": { "type": "object", "description": "Lightweight request type for updating a node's size and position.\n\nUsed for subnet resize operations - instead of sending the entire topology,\nonly sends the node ID, new size, and new position.\nFixes HTTP 413 errors on resize operations.", "required": [ "network_id", "node_id", "size", "position" ], "properties": { "network_id": { "type": "string", "format": "uuid", "description": "Network ID for authorization" }, "node_id": { "type": "string", "format": "uuid", "description": "ID of the node to update" }, "position": { "$ref": "#/components/schemas/Ixy", "description": "New position for the node" }, "size": { "$ref": "#/components/schemas/Uxy", "description": "New size for the node" } } }, "TopologyOptions": { "type": "object", "required": [ "local", "request" ], "properties": { "local": { "$ref": "#/components/schemas/TopologyLocalOptions" }, "request": { "$ref": "#/components/schemas/TopologyRequestOptions" } } }, "TopologyRebuildRequest": { "type": "object", "description": "Lightweight request type for topology rebuild/refresh operations.\n\nThis type only includes the fields actually needed by the server - entity data\n(hosts, ip_addresses, services, etc.) is fetched fresh from the database.\nUsing this instead of the full Topology dramatically reduces payload size\nfor large networks (from MBs to KBs), fixing HTTP 413 errors.", "required": [ "network_id", "options" ], "properties": { "edges": { "type": "array", "items": { "$ref": "#/components/schemas/Edge" }, "description": "Existing edges for reference during rebuild" }, "network_id": { "type": "string", "format": "uuid", "description": "Network ID for authorization and data fetching" }, "nodes": { "type": "array", "items": { "$ref": "#/components/schemas/Node" }, "description": "Existing nodes for position preservation during rebuild" }, "options": { "$ref": "#/components/schemas/TopologyOptions", "description": "Topology options for graph building" } } }, "TopologyRequestOptions": { "type": "object", "required": [ "hide_ports" ], "properties": { "container_rules": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/IdentifiedRule_ContainerRule" } }, "propertyNames": { "type": "string", "description": "Which topology view is being rendered", "enum": [ "L3Logical", "L2Physical", "Workloads", "Application" ] } }, "element_rules": { "type": "array", "items": { "$ref": "#/components/schemas/IdentifiedRule_ElementRule" } }, "hide_ports": { "type": "boolean" }, "hide_service_categories": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceCategory" } }, "propertyNames": { "type": "string", "description": "Which topology view is being rendered", "enum": [ "L3Logical", "L2Physical", "Workloads", "Application" ] } }, "view": { "$ref": "#/components/schemas/TopologyView" } } }, "TopologyTagFilter": { "type": "object", "description": "Filter settings for hiding entities by tag in topology visualization.", "properties": { "hidden_host_tag_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Host tag IDs to hide (hosts with these tags will fade out)" }, "hidden_service_tag_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Service tag IDs to hide (services with these tags will be hidden from nodes)" }, "hidden_subnet_tag_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Subnet tag IDs to hide (subnets with these tags will fade out)" } } }, "TopologyView": { "type": "string", "description": "Which topology view is being rendered", "enum": [ "L3Logical", "L2Physical", "Workloads", "Application" ] }, "TransportProtocol": { "type": "string", "enum": [ "Udp", "Tcp" ] }, "UpdateHostRequest": { "type": "object", "description": "Request type for updating a host with its children.\nUses the same input types as CreateHostRequest.\nServer will sync children (create new, update existing, delete removed) only if provided.", "required": [ "id", "name", "hidden", "tags" ], "properties": { "credential_assignments": { "type": [ "array", "null" ], "items": { "$ref": "#/components/schemas/CredentialAssignment" }, "description": "Credential assignments for this host.\nIf provided, replaces all existing credential assignments." }, "description": { "type": [ "string", "null" ] }, "expected_updated_at": { "type": [ "string", "null" ], "format": "date-time", "description": "Optional: expected updated_at timestamp for optimistic locking." }, "hidden": { "type": "boolean" }, "hostname": { "type": [ "string", "null" ] }, "id": { "type": "string", "format": "uuid" }, "ip_addresses": { "type": [ "array", "null" ], "items": { "$ref": "#/components/schemas/IPAddressInput" }, "description": "Interfaces to sync with this host.\nIf Some, server will create/update/delete to match this list.\nIf None, existing ip_addresses are preserved." }, "name": { "type": "string" }, "ports": { "type": [ "array", "null" ], "items": { "$ref": "#/components/schemas/PortInput" }, "description": "Ports to sync with this host.\nIf Some, server will create/update/delete to match this list.\nIf None, existing ports are preserved." }, "services": { "type": [ "array", "null" ], "items": { "$ref": "#/components/schemas/ServiceInput" }, "description": "Services to sync with this host.\nIf Some, server will create/update/delete to match this list.\nIf None, existing services are preserved." }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "virtualization": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/HostVirtualization" } ] } } }, "UpdatePasswordRequest": { "type": "object", "required": [ "new_password" ], "properties": { "current_password": { "type": [ "string", "null" ], "description": "Current password — required if the user already has a password set.\nNot required for OIDC-only users adding their first password." }, "new_password": { "type": "string", "description": "New password to set" } } }, "UseCase": { "type": "string", "enum": [ "homelab", "internal_it", "msp", "other" ] }, "User": { "allOf": [ { "$ref": "#/components/schemas/UserBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "UserApiKey": { "allOf": [ { "$ref": "#/components/schemas/UserApiKeyBase" }, { "type": "object", "required": [ "id", "updated_at", "created_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "UserApiKeyBase": { "type": "object", "required": [ "key", "name", "user_id", "organization_id", "last_used", "tags" ], "properties": { "expires_at": { "type": [ "string", "null" ], "format": "date-time" }, "is_enabled": { "type": "boolean" }, "key": { "type": "string", "readOnly": true }, "last_used": { "type": [ "string", "null" ], "format": "date-time", "readOnly": true }, "name": { "type": "string" }, "network_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Network IDs this key has access to (hydrated from junction table)" }, "organization_id": { "type": "string", "format": "uuid" }, "permissions": { "$ref": "#/components/schemas/UserOrgPermissions" }, "tags": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "user_id": { "type": "string", "format": "uuid" } } }, "UserApiKeyResponse": { "type": "object", "description": "Response for user API key creation/rotation\nContains the full API key record plus the plaintext key (shown only once)", "required": [ "api_key", "key" ], "properties": { "api_key": { "$ref": "#/components/schemas/UserApiKey" }, "key": { "type": "string", "description": "The plaintext API key - only returned once during creation or rotation" } } }, "UserBase": { "type": "object", "required": [ "email", "organization_id", "permissions", "network_ids" ], "properties": { "email": { "type": "string" }, "email_verified": { "type": "boolean", "description": "Whether the user has verified their email address" }, "has_password": { "type": "boolean", "description": "Whether the user has a password set — computed from password_hash, never stored in DB", "readOnly": true }, "network_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "oidc_linked_at": { "type": [ "string", "null" ], "format": "date-time" }, "oidc_provider": { "type": [ "string", "null" ] }, "organization_id": { "type": "string", "format": "uuid" }, "permissions": { "$ref": "#/components/schemas/UserOrgPermissions" }, "terms_accepted_at": { "type": [ "string", "null" ], "format": "date-time", "readOnly": true } } }, "UserOrgPermissions": { "type": "string", "enum": [ "Owner", "Admin", "Member", "Viewer" ] }, "Uxy": { "type": "object", "description": "2D unsigned coordinate. Used for node positions and sizes.\nElement node sizes are computed by the frontend (elkjs); the backend\nsets `Uxy::default()` for element nodes.", "required": [ "x", "y" ], "properties": { "x": { "type": "integer", "minimum": 0 }, "y": { "type": "integer", "minimum": 0 } } }, "VerifyEmailRequest": { "type": "object", "description": "Request to verify email using token", "required": [ "token" ], "properties": { "token": { "type": "string" } } }, "VersionHealthStatus": { "type": "string", "description": "Health status for daemon versions", "enum": [ "Current", "Outdated", "Deprecated" ] }, "VersionInfo": { "type": "object", "description": "Version information for API compatibility checking", "required": [ "api_version", "server_version" ], "properties": { "api_version": { "type": "integer", "format": "int32", "description": "Current API version (integer, increments on breaking changes)", "minimum": 0 }, "min_compatible_client": { "type": [ "string", "null" ], "description": "Minimum client version that can use this API (optional, for future use)" }, "server_version": { "type": "string", "description": "Server version (semver)", "example": "0.12.10" } } }, "Vlan": { "allOf": [ { "$ref": "#/components/schemas/VlanBase" }, { "type": "object", "required": [ "id", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time", "readOnly": true }, "id": { "type": "string", "format": "uuid", "readOnly": true }, "updated_at": { "type": "string", "format": "date-time", "readOnly": true } } } ] }, "VlanBase": { "type": "object", "required": [ "vlan_number", "name", "network_id", "organization_id" ], "properties": { "description": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "network_id": { "type": "string", "format": "uuid" }, "organization_id": { "type": "string", "format": "uuid" }, "source": { "$ref": "#/components/schemas/EntitySource" }, "vlan_number": { "type": "integer", "format": "int32", "description": "The 802.1Q VLAN number (1-4094)", "minimum": 0 } } }, "VlanDiscoveryItem": { "type": "object", "required": [ "vlan_number", "name" ], "properties": { "name": { "type": "string" }, "vlan_number": { "type": "integer", "format": "int32", "minimum": 0 } } }, "VlanDiscoveryRequest": { "type": "object", "description": "Request body for daemon VLAN discovery upsert", "required": [ "network_id", "vlans" ], "properties": { "network_id": { "type": "string", "format": "uuid" }, "vlans": { "type": "array", "items": { "$ref": "#/components/schemas/VlanDiscoveryItem" } } } }, "VlanDiscoveryResponse": { "type": "object", "description": "Response for discovery upsert", "required": [ "vlans" ], "properties": { "vlans": { "type": "array", "items": { "$ref": "#/components/schemas/VlanDiscoveryResponseItem" }, "description": "Mapping of vlan_number → VLAN entity UUID" } } }, "VlanDiscoveryResponseItem": { "type": "object", "required": [ "vlan_number", "id" ], "properties": { "id": { "type": "string", "format": "uuid" }, "vlan_number": { "type": "integer", "format": "int32", "minimum": 0 } } }, "VlanOrderField": { "type": "string", "enum": [ "created_at", "name", "vlan_number", "updated_at" ] } }, "securitySchemes": { "daemon_api_key": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Daemon API key (Bearer scp_d_...). Requires X-Daemon-ID header." }, "session": { "type": "apiKey", "in": "cookie", "name": "session_id", "description": "Browser session cookie. Obtained via /api/auth/login." }, "user_api_key": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "User API key (Bearer scp_u_...). Create in Platform > API Keys." } } }, "tags": [ { "name": "Bindings", "description": "Service bindings linking services to IP addresses and/or ports. Defines where a service is accessible." }, { "name": "Daemons", "description": "Daemons are scanning agents that connect to the server to perform network discovery." }, { "name": "Daemon API Keys", "description": "API keys for daemon authentication. Create and manage keys that allow daemons to communicate with the server." }, { "name": "Discoveries", "description": "Network discovery operations. Trigger and monitor scans that detect hosts, services, and network topology." }, { "name": "Dependencies", "description": "Service dependency relationships. Define how services depend on each other." }, { "name": "Hosts", "description": "Network hosts (devices). Manage discovered or manually created hosts on your network." }, { "name": "Interfaces", "description": "SNMP ifTable entries. Physical and logical interfaces discovered via SNMP on hosts." }, { "name": "IP Addresses", "description": "IP addresses assigned to hosts. Each address belongs to a host and a subnet, optionally has a MAC address, and represents an observed or configured address on the network." }, { "name": "Invites", "description": "Organization invitations. Invite users to join your organization." }, { "name": "Networks", "description": "Network containers. Top-level organizational unit that contains subnets, hosts, and other entities." }, { "name": "Organizations", "description": "Manage organization settings." }, { "name": "Ports", "description": "Ports that have been scanned and found open on a host." }, { "name": "Services", "description": "Services running on hosts. Detected or manually added services like databases, web servers, etc." }, { "name": "Shares", "description": "Shared network views. Create read-only shareable links to your network topology." }, { "name": "Credentials", "description": "Credentials for network device discovery and management. Supports SNMP, Docker proxy, and other credential types." }, { "name": "Subnets", "description": "IP subnets within networks. Define address ranges and organize hosts by subnet." }, { "name": "Tags", "description": "Custom tags for categorization. Apply labels to entities for filtering and organization." }, { "name": "Topologies", "description": "Network topology maps showing host relationships and connections." }, { "name": "Users", "description": "User account management. Manage user profiles and permissions within organizations." }, { "name": "User API Keys", "description": "User API keys for programmatic access. Create and manage personal API keys with scoped permissions." }, { "name": "Vlans", "description": "VLANs (802.1Q virtual LANs) defined or discovered on the network. Each VLAN has a number (1-4094), a name, and an optional description, and is referenced by interfaces that participate in it." }, { "name": "auth", "description": "Authentication and session management. Handle user login, logout, and session state." }, { "name": "config", "description": "Server configuration. Public configuration settings for client applications." }, { "name": "github", "description": "GitHub integration endpoints." }, { "name": "internal", "description": "Internal endpoints for system operations. Not part of the public API." }, { "name": "metadata", "description": "Entity metadata registry. Schema information for all entity types in the system." }, { "name": "system", "description": "System information endpoints. Version and compatibility checking." } ] }