{"swagger": "2.0", "info": {"title": "Hippius API", "description": "# Hippius Backend API Documentation\n\n## Authentication\nAll endpoints require authentication using a Bearer token:\n```\nAuthorization: Token your-api-token\n```\nGet your token from the authentication endpoints.\n\n## Payment System (Stripe Integration)\n\n### Quick Start - Subscription (Recommended)\n```javascript\n// 1. Get available plans\nGET /api/billing/stripe/subscription-plans/\n\n// 2. Create subscription \nPOST /api/billing/stripe/create-subscription/\n{\n  \"price_id\": \"price_1234567890abcdef\"\n}\n\n// 3. Redirect user to returned checkout_url\n// 4. Credits automatically added monthly\n```\n\n### Quick Start - One-time Payment\n```javascript\n// Create one-time payment\nPOST /api/billing/stripe/create-checkout-session/\n{\n  \"amount\": 50\n}\n\n// Redirect user to returned checkout_url\n// Credits added immediately after payment\n```\n\n### Payment Flow\n1. **Create Payment** → Get Stripe Checkout URL\n2. **User Pays** → Stripe processes payment securely  \n3. **Webhook Fires** → Backend receives payment confirmation\n4. **Credits Minted** → Credits automatically added to user account\n5. **Complete** → User can use credits in the platform\n\n", "termsOfService": "https://www.google.com/policies/terms/", "contact": {"email": "contact@hippius.local"}, "license": {"name": "BSD License"}, "version": "v1"}, "host": "api.hippius.com", "schemes": ["https"], "basePath": "/api", "consumes": ["application/json"], "produces": ["application/json"], "securityDefinitions": {"Bearer": {"type": "apiKey", "name": "Authorization", "in": "header"}}, "security": [{"Bearer": []}], "paths": {"/auth/exchange/": {"post": {"operationId": "exchangeAuthCode", "description": "Exchange a one-time auth code (from social login redirect) for an API token.\nCodes are single-use and expire after ~2 minutes.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/AuthCodeExchange"}}], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"token": {"description": "DRF token", "type": "string"}, "user": {"type": "object", "properties": {"id": {"type": "integer"}, "username": {"type": "string"}, "email": {"type": "string"}, "substrate_address": {"description": "User substrate SS58 address", "type": "string"}}}}}}, "400": {"description": "Invalid or expired code"}}, "tags": ["Authentication"]}, "parameters": []}, "/auth/mnemonic/": {"post": {"operationId": "auth_mnemonic_create", "description": "Request a challenge for mnemonic-based authentication", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["address", "substrate_address"], "type": "object", "properties": {"address": {"description": "Ethereum address derived from mnemonic", "type": "string"}, "substrate_address": {"description": "Substrate address (SS58 format, network 42)", "type": "string"}}}}], "responses": {"200": {"description": "Challenge generated successfully", "schema": {"type": "object", "properties": {"challenge": {"type": "string"}, "message": {"type": "string"}, "expires_in": {"type": "integer"}}}}, "400": {"description": "Bad Request - Missing or invalid addresses"}}, "tags": ["auth"]}, "parameters": []}, "/auth/token/": {"get": {"operationId": "auth_token_list", "description": "Get your master authentication token.\n\nExample:\n```bash\ncurl -X GET \"https://api.hippius.com/api/auth/token/\" \\\n     -H \"Authorization: Bearer your-auth-token\"\n```\n", "parameters": [], "responses": {"200": {"description": "Master token details", "schema": {"type": "object", "properties": {"access_token": {"description": "The JWT access token", "type": "string"}, "refresh_token": {"description": "The JWT refresh token", "type": "string"}, "expires_in": {"type": "integer"}, "token_type": {"type": "string"}, "role": {"type": "string"}, "scope": {"type": "string"}}}}, "401": {"description": "Authentication credentials were not provided or are invalid"}}, "tags": ["auth"]}, "parameters": []}, "/auth/verify-token/": {"post": {"operationId": "auth_verify-token_create", "description": "Verify a user's DRF auth token", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["token"], "type": "object", "properties": {"token": {"description": "The DRF auth token to verify", "type": "string"}}}}], "responses": {"200": {"description": "Token is valid", "schema": {"type": "object", "properties": {"valid": {"type": "boolean"}, "user_id": {"type": "integer"}, "username": {"type": "string"}, "substrate_address": {"type": "string"}}}}, "404": {"description": "Not Found - Token is invalid or does not exist"}, "400": {"description": "Bad Request - Missing token parameter"}}, "tags": ["auth"]}, "parameters": []}, "/auth/verify/": {"post": {"operationId": "auth_verify_create", "description": "Verify signature and authenticate user", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["signature", "address", "substrate_address"], "type": "object", "properties": {"signature": {"description": "Signature of the challenge message", "type": "string"}, "address": {"description": "Ethereum address used in the challenge request", "type": "string"}, "substrate_address": {"description": "Substrate address (SS58 format, network 42)", "type": "string"}, "referral_code": {"description": "Optional referral code (only processed for new users)", "type": "string"}}}}], "responses": {"200": {"description": "Authentication successful", "schema": {"type": "object", "properties": {"token": {"type": "string"}, "user_id": {"type": "integer"}, "username": {"type": "string"}, "substrate_address": {"type": "string"}, "is_new": {"type": "boolean"}}}}, "400": {"description": "Bad Request - Missing parameters or invalid signature"}, "401": {"description": "Unauthorized - Invalid signature"}}, "tags": ["auth"]}, "parameters": []}, "/billing/credits/balance/": {"get": {"operationId": "billing_credits_balance_list", "summary": "Get Credit Balance", "description": "\nGet credit balance information.\n\n**Regular users**: Returns their own credit balance.\n\n**Validators/Admins** (via service token or admin user):\n- Without `account_ss58`: Returns paginated list of ALL user credit balances\n- With `account_ss58`: Returns the credit balance for the specified account\n", "parameters": [{"name": "account_ss58", "in": "query", "description": "(Validator/Admin only) SS58 address to query balance for. If omitted, validators get a paginated list of all balances.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "(Validator/Admin only) Page number for paginated results.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "(Validator/Admin only) Number of results per page (default: 50, max: 200).", "required": false, "type": "integer"}], "responses": {"200": {"description": "Credit balance(s)", "schema": {"type": "object", "properties": {"balance": {"description": "Decimal credits (for single user)", "type": "string"}, "last_updated": {"type": "string", "format": "date-time"}, "account_ss58": {"description": "SS58 address (for validator queries)", "type": "string"}}}}, "401": {"description": "Unauthorized"}, "404": {"description": "Account not found"}}, "tags": ["billing"]}, "parameters": []}, "/billing/latest-tao-price/": {"get": {"operationId": "billing_latest-tao-price_list", "summary": "Get Latest TAO Price", "description": "\nGet the latest TAO price in USD.\nThis endpoint is public and does not require authentication.\nReturns the most recent price based on creation time.\n", "parameters": [], "responses": {"200": {"description": "Latest TAO price information", "schema": {"$ref": "#/definitions/TaoPrice"}, "examples": {"application/json": {"price_usd": "1.23", "block_number": 12345, "created_at": "2025-01-18T18:17:19+01:00"}}}, "404": {"description": "No price data available", "schema": {"type": "object", "properties": {"error": {"type": "string"}}, "example": {"error": "No price data available"}}}}, "tags": ["billing"]}, "parameters": []}, "/billing/stripe/active-subscription/": {"get": {"operationId": "get_active_subscription", "description": "\nGet active subscription details for the authenticated user.\n\nThis endpoint returns detailed information about the user's current subscription,\nincluding plan details, billing cycle, next payment date, and status.\n\n## Quick Start - Get Active Subscription\n\n```bash\n# Get active subscription details\ncurl -X GET \"https://api.hippius.com/api/billing/stripe/active-subscription/\" \\\n     -H \"Authorization: Token your-api-token\"\n\n# Response if user has active subscription:\n# {\n#   \"subscription\": {\n#     \"id\": \"sub_1234567890abcdef\",\n#     \"status\": \"active\",\n#     \"plan_name\": \"Pro Plan\",\n#     \"amount\": 90.00,\n#     \"currency\": \"usd\",\n#     \"interval\": \"month\",\n#     \"current_period_start\": \"2025-06-01T00:00:00Z\",\n#     \"current_period_end\": \"2025-07-01T00:00:00Z\",\n#     \"cancel_at_period_end\": false,\n#     \"credits_per_billing\": 100\n#   },\n#   \"has_subscription\": true\n# }\n\n# Response if user has no subscription:\n# {\n#   \"subscription\": null,\n#   \"has_subscription\": false,\n#   \"message\": \"No active subscription found\"\n# }\n```\n\n**Security:** Users can only see their own subscription details.\n\n**Response includes:**\n- Subscription status and ID\n- Plan name and pricing details\n- Current billing period information\n- Cancellation status\n- Credits per billing cycle\n", "parameters": [], "responses": {"200": {"description": "Subscription details retrieved successfully", "schema": {"type": "object", "properties": {"subscription": {"type": "object", "properties": {"id": {"description": "Stripe Subscription ID", "type": "string"}, "status": {"description": "Subscription status", "type": "string"}, "plan_name": {"description": "Name of the subscription plan", "type": "string"}, "amount": {"description": "Amount per billing cycle", "type": "number"}, "currency": {"description": "Currency code", "type": "string"}, "interval": {"description": "Billing interval", "type": "string"}, "current_period_start": {"description": "Current billing period start", "type": "string"}, "current_period_end": {"description": "Current billing period end", "type": "string"}, "cancel_at_period_end": {"description": "Whether subscription will cancel at period end", "type": "boolean"}, "credits_per_billing": {"description": "Credits added per billing cycle", "type": "integer"}}, "nullable": true}, "has_subscription": {"description": "Whether user has an active subscription", "type": "boolean"}, "message": {"description": "Additional message (when no subscription)", "type": "string"}}, "example": {"subscription": {"id": "sub_1234567890abcdef", "status": "active", "plan_name": "Pro Plan", "amount": 90.0, "currency": "usd", "interval": "month", "current_period_start": "2025-06-01T00:00:00Z", "current_period_end": "2025-07-01T00:00:00Z", "cancel_at_period_end": false, "credits_per_billing": 100}, "has_subscription": true}}}, "503": {"description": "Stripe is not properly configured", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Stripe is not properly configured"}}}}, "tags": ["Subscription Management"]}, "parameters": []}, "/billing/stripe/create-checkout-session/": {"post": {"operationId": "create_checkout_session", "description": "\nCreate a Stripe Checkout Session for one-time credit purchase.\n\nThis endpoint creates a secure Stripe Checkout session where users can purchase credits.\nCredits are added at a 1:1 ratio with USD (1 USD = 1 credit).\n\n## Quick Start - One-time Payment\n\n```bash\n# Create one-time payment\ncurl -X POST \"https://api.hippius.com/api/billing/stripe/create-checkout-session/\" \\\n     -H \"Authorization: Token your-api-token\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n       \"amount\": 50.00,\n       \"success_url\": \"https://app.hippius.com/payment/success\",\n       \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n     }'\n\n# Response:\n# {\n#   \"checkout_url\": \"https://checkout.stripe.com/pay/cs_test_a1b2c3d4e5f6g7h8i9j0\"\n# }\n\n# Redirect user to returned checkout_url\n# Credits added immediately after payment\n```\n\n**Process:**\n1. User calls this endpoint with desired amount\n2. Receives a checkout URL \n3. User completes payment on Stripe's secure page\n4. Credits are automatically minted to user's account\n\n**Example Request:**\n```json\n{\n  \"amount\": 50.00,\n  \"success_url\": \"https://app.hippius.com/payment/success\",\n  \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n}\n```\n", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["amount"], "type": "object", "properties": {"amount": {"description": "Amount in USD to charge (minimum $20, maximum $10,000). 1 USD = 1 Credit", "type": "number", "maximum": 10000.0, "minimum": 20.0}, "success_url": {"description": "URL to redirect to on successful payment", "type": "string", "format": "uri"}, "cancel_url": {"description": "URL to redirect to on payment cancellation", "type": "string", "format": "uri"}}, "additionalProperties": false, "example": {"amount": 50.0, "success_url": "https://app.hippius.com/payment/success", "cancel_url": "https://app.hippius.com/payment/cancel"}}}], "responses": {"200": {"description": "Checkout session created successfully", "schema": {"type": "object", "properties": {"checkout_url": {"description": "Stripe Checkout URL to redirect user to", "type": "string"}}, "example": {"checkout_url": "https://checkout.stripe.com/pay/cs_test_a1b2c3d4e5f6g7h8i9j0"}}}, "400": {"description": "Bad request - Invalid amount or missing parameters", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Minimum amount is $20"}}}, "429": {"description": "Rate limit exceeded", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Rate limit exceeded. Please wait before creating another payment."}}}, "503": {"description": "Stripe is not properly configured", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Stripe is not properly configured"}}}}, "tags": ["Payments"]}, "parameters": []}, "/billing/stripe/create-payment/": {"post": {"operationId": "create_payment", "description": "\nUnified endpoint for creating either subscription or one-time payment.\n\nThis is a convenience endpoint that routes to the appropriate payment method\nbased on the `payment_type` parameter. It supports both one-time purchases\nand recurring subscriptions.\n\n## Quick Start - Unified Payment Endpoint\n\n```bash\n# For Subscription (Recommended):\ncurl -X POST \"https://api.hippius.com/api/billing/stripe/create-payment/\" \\\n     -H \"Authorization: Token your-api-token\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n       \"payment_type\": \"subscription\",\n       \"price_id\": \"price_1234567890abcdef\",\n       \"success_url\": \"https://app.hippius.com/payment/success\",\n       \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n     }'\n\n# For One-time Payment:\ncurl -X POST \"https://api.hippius.com/api/billing/stripe/create-payment/\" \\\n     -H \"Authorization: Token your-api-token\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n       \"payment_type\": \"onetime\",\n       \"amount\": 50.00,\n       \"success_url\": \"https://app.hippius.com/payment/success\",\n       \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n     }'\n\n# Both return:\n# {\n#   \"checkout_url\": \"https://checkout.stripe.com/pay/cs_test_...\",\n#   \"session_id\": \"cs_test_...\" (subscriptions only)\n# }\n```\n\n**Example - Subscription:**\n```json\n{\n  \"payment_type\": \"subscription\",\n  \"price_id\": \"price_1234567890abcdef\",\n  \"success_url\": \"https://app.hippius.com/payment/success\",\n  \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n}\n```\n\n**Example - One-time Payment:**\n```json\n{\n  \"payment_type\": \"onetime\",\n  \"amount\": 50.00,\n  \"success_url\": \"https://app.hippius.com/payment/success\",\n  \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n}\n```\n\n**Recommendation:** Use subscription for regular users to ensure they never run out of credits.\n", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["payment_type"], "type": "object", "properties": {"payment_type": {"description": "Payment type: \"subscription\" (recommended) or \"onetime\"", "type": "string", "enum": ["subscription", "onetime"]}, "price_id": {"description": "Required for subscriptions (get from /subscription-plans/). Must start with \"price_\"", "type": "string", "pattern": "^price_[0-9A-Za-z]{14,}$"}, "amount": {"description": "Required for one-time payments (minimum $20, maximum $10,000)", "type": "number", "maximum": 10000.0, "minimum": 20.0}, "success_url": {"description": "URL to redirect to on successful payment", "type": "string", "format": "uri"}, "cancel_url": {"description": "URL to redirect to on payment cancellation", "type": "string", "format": "uri"}}, "additionalProperties": false, "example": {"payment_type": "subscription", "price_id": "price_1234567890abcdef", "success_url": "https://app.hippius.com/payment/success", "cancel_url": "https://app.hippius.com/payment/cancel"}}}], "responses": {"200": {"description": "Payment session created successfully", "schema": {"type": "object", "properties": {"checkout_url": {"description": "Stripe Checkout URL to redirect user to", "type": "string"}, "session_id": {"description": "Stripe Checkout Session ID (for subscriptions)", "type": "string"}}, "example": {"checkout_url": "https://checkout.stripe.com/pay/cs_test_unified_a1b2c3d4e5f6", "session_id": "cs_test_a1b2c3d4e5f6g7h8i9j0"}}}, "400": {"description": "Bad request - Invalid payment_type or missing required parameters", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}, "recommendation": {"description": "Recommendation for fixing the error", "type": "string"}}, "example": {"error": "Invalid payment_type. Use \"subscription\" or \"onetime\"", "recommendation": "Consider using \"subscription\" for better value and convenience"}}}, "503": {"description": "Stripe is not properly configured", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Stripe is not properly configured"}}}}, "tags": ["Payments"]}, "parameters": []}, "/billing/stripe/create-subscription/": {"post": {"operationId": "create_subscription", "description": "\nCreate a Stripe Checkout Session for recurring subscription.\n\nThis endpoint creates a subscription that automatically bills the user monthly/yearly.\nCredits are automatically added to the user's account on each billing cycle.\n\n## Quick Start - Subscription (Recommended)\n\n```bash\n# 1. Get available plans\ncurl -X GET \"https://api.hippius.com/api/billing/stripe/subscription-plans/\" \\\n     -H \"Authorization: Token your-api-token\"\n\n# 2. Create subscription with chosen price_id\ncurl -X POST \"https://api.hippius.com/api/billing/stripe/create-subscription/\" \\\n     -H \"Authorization: Token your-api-token\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n       \"price_id\": \"price_1234567890abcdef\",\n       \"success_url\": \"https://app.hippius.com/payment/success\",\n       \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n     }'\n\n# Response:\n# {\n#   \"checkout_url\": \"https://checkout.stripe.com/pay/cs_test_subscription_a1b2c3d4e5f6\",\n#   \"session_id\": \"cs_test_a1b2c3d4e5f6g7h8i9j0\"\n# }\n\n# 3. Redirect user to returned checkout_url\n# 4. Credits automatically added monthly/yearly\n```\n\n**Process:**\n1. Get available price IDs from `/subscription-plans/` endpoint\n2. Create subscription with chosen price_id\n3. User completes payment setup on Stripe's page\n4. Credits are automatically added monthly/yearly\n\n**Example Request:**\n```json\n{\n  \"price_id\": \"price_1234567890abcdef\",\n  \"payment_method_types\": [\"card\"],\n  \"success_url\": \"https://app.hippius.com/payment/success\",\n  \"cancel_url\": \"https://app.hippius.com/payment/cancel\"\n}\n```\n", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["price_id"], "type": "object", "properties": {"price_id": {"description": "Stripe Price ID for the subscription plan (get from /subscription-plans/). Must start with \"price_\"", "type": "string", "pattern": "^price_[0-9A-Za-z]{14,}$"}, "payment_method_types": {"description": "Payment methods to accept (default: [\"card\"])", "type": "array", "items": {"type": "string", "enum": ["card", "sepa_debit", "ideal", "bancontact", "giropay", "p24", "sofort", "eps", "fpx", "grabpay"]}, "default": ["card"], "maxItems": 5}, "success_url": {"description": "URL to redirect to on successful payment", "type": "string", "format": "uri"}, "cancel_url": {"description": "URL to redirect to on payment cancellation", "type": "string", "format": "uri"}}, "additionalProperties": false, "example": {"price_id": "price_1234567890abcdef", "payment_method_types": ["card"], "success_url": "https://app.hippius.com/payment/success", "cancel_url": "https://app.hippius.com/payment/cancel"}}}], "responses": {"200": {"description": "Subscription checkout session created successfully", "schema": {"type": "object", "properties": {"checkout_url": {"description": "Stripe Checkout URL to redirect user to", "type": "string"}, "session_id": {"description": "Stripe Checkout Session ID", "type": "string"}}, "example": {"checkout_url": "https://checkout.stripe.com/pay/cs_test_subscription_a1b2c3d4e5f6", "session_id": "cs_test_a1b2c3d4e5f6g7h8i9j0"}}}, "400": {"description": "Bad request - Missing price_id or invalid parameters", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "price_id is required"}}}, "429": {"description": "Rate limit exceeded", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Rate limit exceeded. Please wait before creating another subscription."}}}, "503": {"description": "Stripe is not properly configured", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Stripe is not properly configured"}}}}, "tags": ["Payments"]}, "parameters": []}, "/billing/stripe/customer-portal/": {"post": {"operationId": "get_customer_portal_url", "description": "\nGet Stripe Customer Portal URL for subscription management.\n\nThis endpoint generates a secure URL to Stripe's Customer Portal where users can:\n- Cancel their subscription\n- Change their payment method  \n- Update billing information\n- View billing history\n- Download invoices\n\n## Quick Start - Get Customer Portal URL\n\n```bash\n# Get customer portal URL\ncurl -X POST \"https://api.hippius.com/api/billing/stripe/customer-portal/\" \\\n     -H \"Authorization: Token your-api-token\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n       \"return_url\": \"https://app.hippius.com/billing\"\n     }'\n\n# Response:\n# {\n#   \"portal_url\": \"https://billing.stripe.com/session/test_portal_abc123\"\n# }\n\n# Redirect user to returned portal_url\n```\n\n**Security:** Only authenticated users can access their own customer portal.\nThe portal is automatically scoped to the authenticated user's Stripe customer.\n\n**Process:**\n1. User calls this endpoint \n2. Receives a secure portal URL\n3. User is redirected to Stripe's portal\n4. User can manage subscription, payment methods, etc.\n5. User returns to your app via return_url\n", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"type": "object", "properties": {"return_url": {"description": "URL to redirect user back to after portal session (default: app homepage)", "type": "string", "format": "uri", "default": "https://app.hippius.com/billing"}}, "additionalProperties": false, "example": {"return_url": "https://app.hippius.com/billing"}}}], "responses": {"200": {"description": "Customer portal URL created successfully", "schema": {"type": "object", "properties": {"portal_url": {"description": "Stripe Customer Portal URL to redirect user to", "type": "string"}}, "example": {"portal_url": "https://billing.stripe.com/session/test_portal_abc123"}}}, "400": {"description": "Bad request - User has no Stripe customer record", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "No customer record found. Please create a subscription first."}}}, "503": {"description": "Stripe is not properly configured", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Stripe is not properly configured"}}}}, "tags": ["Subscription Management"]}, "parameters": []}, "/billing/stripe/subscription-plans/": {"get": {"operationId": "subscription_plans", "description": "\nGet available subscription plans from Stripe.\n\nThis endpoint returns all active subscription plans configured in your Stripe account.\nUse the returned `price_id` values to create subscriptions.\n\n## Quick Start - Get Subscription Plans\n\n```bash\n# Get available subscription plans\ncurl -X GET \"https://api.hippius.com/api/billing/stripe/subscription-plans/\" \\\n     -H \"Authorization: Token your-api-token\"\n\n# Response example:\n# {\n#   \"plans\": [\n#     {\n#       \"id\": \"prod_1234567890\",\n#       \"name\": \"Starter Plan\",\n#       \"description\": \"25 credits per month\",\n#       \"price_id\": \"price_1234567890abcdef\",\n#       \"currency\": \"usd\",\n#       \"amount\": 25.00,\n#       \"interval\": \"month\",\n#       \"interval_count\": 1,\n  #       \"credits_per_billing\": 25\n#     }\n#   ],\n#   \"recommendation\": \"Subscriptions offer automatic credit reloading...\"\n# }\n\n# Use the price_id to create a subscription:\n# POST /api/billing/stripe/create-subscription/\n# { \"price_id\": \"price_1234567890abcdef\" }\n```\n\n**Response includes:**\n- Plan details (name, description, pricing)\n- Billing intervals (monthly, yearly)\n- Price IDs for subscription creation\n- Savings calculations vs one-time purchases\n", "parameters": [], "responses": {"200": {"description": "List of available subscription plans", "schema": {"type": "object", "properties": {"plans": {"type": "array", "items": {"type": "object", "properties": {"id": {"description": "Stripe Product ID", "type": "string"}, "name": {"description": "Plan name", "type": "string"}, "description": {"description": "Plan description", "type": "string"}, "price_id": {"description": "Stripe Price ID", "type": "string"}, "currency": {"description": "Currency code", "type": "string"}, "amount": {"description": "Price per billing cycle", "type": "number"}, "interval": {"description": "Billing interval", "type": "string"}, "interval_count": {"description": "Number of intervals", "type": "integer"}, "credits_per_billing": {"description": "Credits added per cycle", "type": "integer"}}}}, "recommendation": {"description": "Recommendation text", "type": "string"}}, "example": {"plans": [{"id": "prod_1234567890", "name": "Starter Plan", "description": "25 credits per month", "price_id": "price_1234567890abcdef", "currency": "usd", "amount": 25.0, "interval": "month", "interval_count": 1, "credits_per_billing": 25}, {"id": "prod_0987654321", "name": "Pro Plan", "description": "100 credits per month", "price_id": "price_0987654321fedcba", "currency": "usd", "amount": 90.0, "interval": "month", "interval_count": 1, "credits_per_billing": 100}], "recommendation": "Subscriptions offer automatic credit reloading and better value than one-time purchases."}}}, "503": {"description": "Stripe is not properly configured", "schema": {"type": "object", "properties": {"error": {"description": "Error message", "type": "string"}}, "example": {"error": "Stripe is not properly configured"}}}}, "tags": ["Payments"]}, "parameters": []}, "/billing/subscriptions/": {"get": {"operationId": "billing_subscriptions_list", "description": "API endpoint for viewing subscriptions (Marketing Dashboard).\nOnly users in the 'Marketing' group (or superusers) can access this.", "parameters": [{"name": "status", "in": "query", "description": "", "required": false, "type": "string"}, {"name": "stripe_subscription_id", "in": "query", "description": "", "required": false, "type": "string"}, {"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"$ref": "#/definitions/SubscriptionModel"}}}}}}, "tags": ["billing"]}, "parameters": []}, "/billing/subscriptions/{id}/": {"get": {"operationId": "billing_subscriptions_read", "description": "API endpoint for viewing subscriptions (Marketing Dashboard).\nOnly users in the 'Marketing' group (or superusers) can access this.", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/SubscriptionModel"}}}, "tags": ["billing"]}, "parameters": [{"name": "id", "in": "path", "description": "A unique integer value identifying this subscription.", "required": true, "type": "integer"}]}, "/billing/substrate-address/": {"get": {"operationId": "billing_substrate-address_list", "description": "Override list to return a single address object instead of a list", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"$ref": "#/definitions/SubstrateAddress"}}}}}}, "tags": ["billing"]}, "parameters": []}, "/billing/substrate-address/{id}/": {"get": {"operationId": "billing_substrate-address_read", "description": "API endpoint for viewing a user's substrate address.\nUsers can only view their own address.", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/SubstrateAddress"}}}, "tags": ["billing"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/billing/transactions/": {"get": {"operationId": "billing_transactions_list", "description": "List all transactions", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "List of transactions", "schema": {"type": "array", "items": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "payment_type": {"type": "string"}, "amount": {"type": "number"}, "currency": {"type": "string"}, "credits": {"type": "integer"}, "status": {"type": "string"}, "created_at": {"type": "string", "format": "date-time"}}}}}}, "tags": ["billing"]}, "post": {"operationId": "billing_transactions_create", "description": "Create a new payment transaction", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["payment_type", "amount", "currency"], "type": "object", "properties": {"payment_type": {"type": "string", "enum": ["stripe", "crypto"]}, "amount": {"description": "Minimum $20 USD equivalent", "type": "number"}, "currency": {"type": "string", "enum": ["USD", "USDC", "USDT", "TAO"]}}}}], "responses": {"201": {"description": "Transaction created", "schema": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "payment_type": {"type": "string"}, "amount": {"type": "number"}, "currency": {"type": "string"}, "credits": {"type": "integer"}, "status": {"type": "string"}, "created_at": {"type": "string", "format": "date-time"}, "payment_address": {"description": "Address to send crypto payment to (only for crypto payments)", "type": "string"}, "client_secret": {"description": "Stripe client secret (only for Stripe payments)", "type": "string"}}}}, "400": {"description": "Bad Request"}, "403": {"description": "Forbidden"}}, "tags": ["billing"]}, "parameters": []}, "/billing/transactions/{id}/": {"get": {"operationId": "billing_transactions_read", "description": "Retrieve a specific transaction", "parameters": [], "responses": {"200": {"description": "Transaction details", "schema": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "payment_type": {"type": "string"}, "amount": {"type": "number"}, "currency": {"type": "string"}, "credits": {"type": "integer"}, "status": {"type": "string"}, "created_at": {"type": "string", "format": "date-time"}, "completed_at": {"type": "string", "format": "date-time"}, "substrate_block_number": {"type": "integer"}, "substrate_success": {"type": "boolean"}}}}, "404": {"description": "Not Found"}}, "tags": ["billing"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/billing/usage/record/": {"post": {"operationId": "billing_usage_record_create", "description": "Record storage usage reported by the validator for a user.\nCreates a StorageUsageRecord and updates UserProfileSummary.\nRequires ServiceToken with validator privileges.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["billing"]}, "parameters": []}, "/infrastructure/certificates/": {"get": {"operationId": "infrastructure_certificates_list", "description": "Get the current user's active Nebula certificate", "parameters": [], "responses": {"200": {"description": "Certificate details", "schema": {"type": "object", "properties": {"certificate_id": {"type": "integer"}, "ca": {"type": "string"}, "cert": {"type": "string"}, "key": {"type": "string"}, "ip": {"type": "string"}, "config": {"type": "string"}, "is_active": {"type": "boolean"}, "expires_at": {"type": "string", "format": "date-time"}, "created_at": {"type": "string", "format": "date-time"}}}}, "404": {"description": "No active certificate found", "schema": {"type": "object", "properties": {"error": {"type": "string"}}}}}, "tags": ["nebula"]}, "parameters": []}, "/infrastructure/certificates/renew/": {"post": {"operationId": "infrastructure_certificates_renew_create", "description": "Renew the current user's Nebula VPN certificate. Uses the same UUID to get a new certificate with same IP.", "parameters": [], "responses": {"200": {"description": "Certificate successfully renewed", "schema": {"type": "object", "properties": {"certificate_id": {"description": "ID of the certificate", "type": "integer"}, "ca": {"description": "CA certificate in PEM format", "type": "string"}, "cert": {"description": "Host certificate in PEM format", "type": "string"}, "key": {"description": "Private key in PEM format", "type": "string"}, "ip": {"description": "Assigned Nebula IP", "type": "string"}, "config": {"description": "Nebula config YAML", "type": "string"}}}}, "404": {"description": "No certificate to renew", "schema": {"type": "object", "properties": {"error": {"type": "string"}}}}, "503": {"description": "CA service unavailable", "schema": {"type": "object", "properties": {"error": {"type": "string"}}}}}, "tags": ["nebula"]}, "parameters": []}, "/infrastructure/certificates/request/": {"post": {"operationId": "infrastructure_certificates_request_create", "description": "Request a new Nebula VPN certificate. Requires at least 10 credits.", "parameters": [], "responses": {"200": {"description": "Certificate successfully issued", "schema": {"type": "object", "properties": {"certificate_id": {"description": "ID of the certificate", "type": "integer"}, "ca": {"description": "CA certificate in PEM format", "type": "string"}, "cert": {"description": "Host certificate in PEM format", "type": "string"}, "key": {"description": "Private key in PEM format", "type": "string"}, "ip": {"description": "Assigned Nebula IP", "type": "string"}, "config": {"description": "Nebula config YAML", "type": "string"}}}}, "402": {"description": "Insufficient credits", "schema": {"type": "object", "properties": {"error": {"type": "string"}, "required_credits": {"type": "integer"}, "current_credits": {"type": "number"}}}}, "503": {"description": "CA service unavailable", "schema": {"type": "object", "properties": {"error": {"type": "string"}}}}}, "tags": ["nebula"]}, "parameters": []}, "/infrastructure/vm/applications/": {"get": {"operationId": "infrastructure_vm_applications_list", "description": "List available VM One-Click Applications (Docker, Postgres, etc.)", "parameters": [], "responses": {"200": {"description": "List of VM applications", "schema": {"type": "array", "items": {"type": "object", "properties": {"id": {"type": "integer"}, "name": {"type": "string"}, "slug": {"type": "string"}, "description": {"type": "string"}, "logo_url": {"type": "string"}}}}}}, "tags": ["vm"]}, "parameters": []}, "/infrastructure/vm/flavors/": {"get": {"operationId": "infrastructure_vm_flavors_list", "description": "List available VM flavors", "parameters": [], "responses": {"200": {"description": "List of VM flavors", "schema": {"type": "array", "items": {"type": "object", "properties": {"id": {"type": "integer"}, "name": {"type": "string"}, "display_name": {"type": "string"}, "cpu_cores": {"type": "integer"}, "memory_mb": {"type": "integer"}, "data_disk_gb": {"type": "integer"}, "credits_per_hour": {"type": "number"}}}}}}, "tags": ["vm"]}, "parameters": []}, "/infrastructure/vm/images/": {"get": {"operationId": "infrastructure_vm_images_list", "description": "List available VM OS images", "parameters": [], "responses": {"200": {"description": "List of VM images", "schema": {"type": "array", "items": {"type": "object", "properties": {"id": {"type": "integer"}, "name": {"type": "string"}, "slug": {"type": "string"}, "description": {"type": "string"}}}}}}, "tags": ["vm"]}, "parameters": []}, "/infrastructure/vm/instances/": {"get": {"operationId": "infrastructure_vm_instances_list", "description": "List user's VM instances", "parameters": [], "responses": {"200": {"description": "List of VMs"}}, "tags": ["vm"]}, "parameters": []}, "/infrastructure/vm/instances/{instance_id}/": {"get": {"operationId": "infrastructure_vm_instances_read", "description": "Get VM instance details", "parameters": [], "responses": {"200": {"description": "VM details"}}, "tags": ["vm"]}, "parameters": [{"name": "instance_id", "in": "path", "required": true, "type": "string"}]}, "/infrastructure/vm/instances/{instance_id}/reboot/": {"post": {"operationId": "infrastructure_vm_instances_reboot_create", "description": "Reboot a VM", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["vm"]}, "parameters": [{"name": "instance_id", "in": "path", "required": true, "type": "string"}]}, "/infrastructure/vm/instances/{instance_id}/start/": {"post": {"operationId": "infrastructure_vm_instances_start_create", "description": "Start a stopped VM", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["vm"]}, "parameters": [{"name": "instance_id", "in": "path", "required": true, "type": "string"}]}, "/infrastructure/vm/instances/{instance_id}/stop/": {"post": {"operationId": "infrastructure_vm_instances_stop_create", "description": "Stop a running VM", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["vm"]}, "parameters": [{"name": "instance_id", "in": "path", "required": true, "type": "string"}]}, "/infrastructure/vm/instances/{instance_id}/terminate/": {"post": {"operationId": "infrastructure_vm_instances_terminate_create", "description": "Terminate a VM (permanent)", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["vm"]}, "parameters": [{"name": "instance_id", "in": "path", "required": true, "type": "string"}]}, "/infrastructure/vm/spawn/": {"post": {"operationId": "infrastructure_vm_spawn_create", "description": "Spawn a new VM instance (async). Requires 10+ credits.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["flavor_id", "image_id", "ssh_public_key"], "type": "object", "properties": {"flavor_id": {"description": "VM flavor ID", "type": "integer"}, "image_id": {"description": "OS image ID", "type": "integer"}, "application_id": {"description": "Optional Application ID (e.g. for Docker, WordPress)", "type": "integer"}, "ssh_public_key": {"description": "SSH public key", "type": "string"}, "name": {"description": "Optional VM name", "type": "string"}}}}], "responses": {"202": {"description": "VM spawn initiated"}, "400": {"description": "Invalid request"}, "402": {"description": "Insufficient credits"}, "403": {"description": "Beta access required"}}, "tags": ["vm"]}, "parameters": []}, "/models/": {"get": {"operationId": "models_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["models"]}, "parameters": []}, "/models/formats/": {"get": {"operationId": "models_formats_list", "description": "Used by the UI to populate filter dropdowns.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["models"]}, "parameters": []}, "/models/{project}/{repo}/": {"get": {"operationId": "models_read", "summary": "List every indexed version (tag + digest) of one repo.", "description": "Ordered by indexed_at desc so the latest sits at the top.\nAnonymous callers only see this if the project is public; authenticated\ncallers additionally see their own private projects.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["models"]}, "parameters": [{"name": "project", "in": "path", "required": true, "type": "string"}, {"name": "repo", "in": "path", "required": true, "type": "string"}]}, "/models/{project}/{repo}/{reference}/": {"get": {"operationId": "models_read", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["models"]}, "parameters": [{"name": "project", "in": "path", "required": true, "type": "string"}, {"name": "repo", "in": "path", "required": true, "type": "string"}, {"name": "reference", "in": "path", "required": true, "type": "string"}]}, "/network/enroll-keys/": {"get": {"operationId": "network_enroll-keys_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "parameters": []}, "/network/enroll-keys/{id}/": {"delete": {"operationId": "network_enroll-keys_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/network/groups/": {"get": {"operationId": "network_groups_list", "description": "Tenant sub-groups (graph segmentation). The default group is listed\nbut cannot be recreated/deleted.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "post": {"operationId": "network_groups_create", "description": "Create a sub-group (a new group node on the canvas) to segment peers and draw finer policies.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["label"], "type": "object", "properties": {"label": {"description": "lowercase alnum/dash, 1-50", "type": "string"}}}}], "responses": {"201": {"description": "", "schema": {"required": ["label"], "type": "object", "properties": {"label": {"description": "lowercase alnum/dash, 1-50", "type": "string"}}}}}, "tags": ["netbird-graph"]}, "parameters": []}, "/network/groups/{id}/": {"delete": {"operationId": "network_groups_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/network/groups/{id}/members/": {"post": {"operationId": "network_groups_members_create", "description": "Add/remove a tenant peer to/from a tenant group (peer placement for\nthe graph). Both peer and group MUST belong to the caller.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/network/infra/enroll/": {"post": {"operationId": "network_infra_enroll_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["network"]}, "parameters": []}, "/network/infra/enrollments/": {"get": {"operationId": "network_infra_enrollments_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "parameters": []}, "/network/infra/enrollments/{id}/": {"delete": {"operationId": "network_infra_enrollments_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/network/me/": {"get": {"operationId": "network_me_list", "description": "Tenant summary (status + counts). 403 until you have provisioned.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["netbird"]}, "parameters": []}, "/network/networks/": {"get": {"operationId": "network_networks_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "post": {"operationId": "network_networks_create", "description": "Create an extra VPC network (a default one exists from provision).", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["name"], "type": "object", "properties": {"name": {"type": "string"}}}}], "responses": {"201": {"description": "", "schema": {"required": ["name"], "type": "object", "properties": {"name": {"type": "string"}}}}}, "tags": ["netbird-vpc"]}, "parameters": []}, "/network/networks/{id}/": {"delete": {"operationId": "network_networks_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/network/networks/{nid}/resources/": {"get": {"operationId": "network_networks_resources_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "post": {"operationId": "network_networks_resources_create", "description": "Add a resource (host/subnet/domain) to a network. `kind` is inferred from `address` if omitted. `groups` (if given) MUST be YOUR group ids.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["name", "address"], "type": "object", "properties": {"name": {"type": "string"}, "address": {"description": "CIDR / IP / domain", "type": "string"}, "kind": {"type": "string", "enum": ["host", "subnet", "domain"]}, "groups": {"type": "array", "items": {"type": "integer"}}}}}], "responses": {"201": {"description": "", "schema": {"required": ["name", "address"], "type": "object", "properties": {"name": {"type": "string"}, "address": {"description": "CIDR / IP / domain", "type": "string"}, "kind": {"type": "string", "enum": ["host", "subnet", "domain"]}, "groups": {"type": "array", "items": {"type": "integer"}}}}}}, "tags": ["netbird-vpc"]}, "parameters": [{"name": "nid", "in": "path", "required": true, "type": "string"}]}, "/network/networks/{nid}/resources/{rid}/": {"patch": {"operationId": "network_networks_resources_partial_update", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "delete": {"operationId": "network_networks_resources_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "nid", "in": "path", "required": true, "type": "string"}, {"name": "rid", "in": "path", "required": true, "type": "string"}]}, "/network/networks/{nid}/routers/": {"get": {"operationId": "network_networks_routers_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "post": {"operationId": "network_networks_routers_create", "description": "Make one of YOUR peers a routing gateway for this network.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["peer"], "type": "object", "properties": {"peer": {"description": "NetbirdPeer id (yours)", "type": "integer"}, "masquerade": {"type": "boolean"}, "metric": {"type": "integer"}}}}], "responses": {"201": {"description": "", "schema": {"required": ["peer"], "type": "object", "properties": {"peer": {"description": "NetbirdPeer id (yours)", "type": "integer"}, "masquerade": {"type": "boolean"}, "metric": {"type": "integer"}}}}}, "tags": ["netbird-vpc"]}, "parameters": [{"name": "nid", "in": "path", "required": true, "type": "string"}]}, "/network/networks/{nid}/routers/{rid}/": {"delete": {"operationId": "network_networks_routers_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "nid", "in": "path", "required": true, "type": "string"}, {"name": "rid", "in": "path", "required": true, "type": "string"}]}, "/network/peers/": {"get": {"operationId": "network_peers_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "parameters": []}, "/network/peers/enroll/": {"post": {"operationId": "network_peers_enroll_create", "description": "Issue a ONE-OFF setup key for one VM. The plaintext `setup_key` + `join_command` are returned ONCE — not re-readable. Optionally scope to one of your sub-groups.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"type": "object", "properties": {"vm_label": {"type": "string"}, "group": {"description": "Optional NetbirdGroup id (yours); default = tenant default group", "type": "integer"}, "vm_ticket_ref": {"type": "string"}}}}], "responses": {"201": {"description": "", "schema": {"type": "object", "properties": {"vm_label": {"type": "string"}, "group": {"description": "Optional NetbirdGroup id (yours); default = tenant default group", "type": "integer"}, "vm_ticket_ref": {"type": "string"}}}}}, "tags": ["netbird"]}, "parameters": []}, "/network/peers/{id}/": {"get": {"operationId": "network_peers_read", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "patch": {"operationId": "network_peers_partial_update", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["network"]}, "delete": {"operationId": "network_peers_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["network"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/network/policies/": {"get": {"operationId": "network_policies_list", "description": "List the tenant's policies (graph edges). Each maps to an edge group->group in /topology/.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["netbird-graph"]}, "post": {"operationId": "network_policies_create", "description": "Create a policy = draw an edge between two of YOUR groups. This is the API call a drag-to-connect gesture fires. Returns the created policy (use its `id` as the edge id).", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["source_group", "dest_group"], "type": "object", "properties": {"name": {"description": "Optional; defaults to <src>-to-<dst>", "type": "string"}, "source_group": {"description": "NetbirdGroup id (MUST be one of YOUR groups — cross-tenant/infra ids are rejected 400). This is the source node you dragged FROM.", "type": "integer"}, "dest_group": {"description": "NetbirdGroup id you dragged TO (same ownership rule).", "type": "integer"}, "protocol": {"type": "string", "enum": ["all", "tcp", "udp", "icmp"]}, "ports": {"description": "e.g. [\"22\",\"443\"] (ignored when protocol=all)", "type": "array", "items": {"type": "string"}}, "bidirectional": {"type": "boolean"}, "action": {"type": "string", "enum": ["accept", "drop"]}, "enabled": {"type": "boolean"}}}}], "responses": {"201": {"description": "", "schema": {"required": ["source_group", "dest_group"], "type": "object", "properties": {"name": {"description": "Optional; defaults to <src>-to-<dst>", "type": "string"}, "source_group": {"description": "NetbirdGroup id (MUST be one of YOUR groups — cross-tenant/infra ids are rejected 400). This is the source node you dragged FROM.", "type": "integer"}, "dest_group": {"description": "NetbirdGroup id you dragged TO (same ownership rule).", "type": "integer"}, "protocol": {"type": "string", "enum": ["all", "tcp", "udp", "icmp"]}, "ports": {"description": "e.g. [\"22\",\"443\"] (ignored when protocol=all)", "type": "array", "items": {"type": "string"}}, "bidirectional": {"type": "boolean"}, "action": {"type": "string", "enum": ["accept", "drop"]}, "enabled": {"type": "boolean"}}}}}, "tags": ["netbird-graph"]}, "parameters": []}, "/network/policies/{id}/": {"patch": {"operationId": "network_policies_partial_update", "description": "Edit an edge. 409 if it is managed (is_managed=true → render read-only in the canvas).", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["source_group", "dest_group"], "type": "object", "properties": {"name": {"description": "Optional; defaults to <src>-to-<dst>", "type": "string"}, "source_group": {"description": "NetbirdGroup id (MUST be one of YOUR groups — cross-tenant/infra ids are rejected 400). This is the source node you dragged FROM.", "type": "integer"}, "dest_group": {"description": "NetbirdGroup id you dragged TO (same ownership rule).", "type": "integer"}, "protocol": {"type": "string", "enum": ["all", "tcp", "udp", "icmp"]}, "ports": {"description": "e.g. [\"22\",\"443\"] (ignored when protocol=all)", "type": "array", "items": {"type": "string"}}, "bidirectional": {"type": "boolean"}, "action": {"type": "string", "enum": ["accept", "drop"]}, "enabled": {"type": "boolean"}}}}], "responses": {"200": {"description": "", "schema": {"required": ["source_group", "dest_group"], "type": "object", "properties": {"name": {"description": "Optional; defaults to <src>-to-<dst>", "type": "string"}, "source_group": {"description": "NetbirdGroup id (MUST be one of YOUR groups — cross-tenant/infra ids are rejected 400). This is the source node you dragged FROM.", "type": "integer"}, "dest_group": {"description": "NetbirdGroup id you dragged TO (same ownership rule).", "type": "integer"}, "protocol": {"type": "string", "enum": ["all", "tcp", "udp", "icmp"]}, "ports": {"description": "e.g. [\"22\",\"443\"] (ignored when protocol=all)", "type": "array", "items": {"type": "string"}}, "bidirectional": {"type": "boolean"}, "action": {"type": "string", "enum": ["accept", "drop"]}, "enabled": {"type": "boolean"}}}}}, "tags": ["netbird-graph"]}, "delete": {"operationId": "network_policies_delete", "description": "Delete an edge (drag-to-remove / del key). 409 if managed.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["netbird-graph"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/network/provision/": {"post": {"operationId": "network_provision_create", "description": "Idempotent. Call once before anything else: creates the caller's NetBird tenant (default group + network + managed intra-tenant policy). No body. Returns the tenant summary.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["netbird"]}, "parameters": []}, "/network/topology/": {"get": {"operationId": "network_topology_list", "description": "The whole graph the drag-and-drop canvas renders, scoped to the caller's tenant. Shape:\n  nodes: [{id:'group:1'|'peer:2'|'resource:3'|'router:4', type:'group'|'peer'|'resource'|'router', label, meta}]\n  edges: [{id:'policy:9'|'route:..'|'rbind:..', type:'policy'|'route'|'router-binding', source, target, meta}]\nNode/edge ids are 'kind:pk'. To mutate a 'policy' edge use /policies/ with meta.policy_id. Edges with meta.is_managed=true are read-only. Re-GET this after any mutation (or update optimistically).", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["netbird-graph"]}, "parameters": []}, "/notifications/settings/": {"get": {"operationId": "notifications_settings_read", "summary": "Get Notification Preferences", "description": "Retrieve the current user's notification settings.", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/NotificationPreferences"}}}, "tags": ["notifications"]}, "put": {"operationId": "notifications_settings_update", "summary": "Update Notification Preferences (Full)", "description": "Update all notification settings for the current user.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/NotificationPreferences"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/NotificationPreferences"}}}, "tags": ["notifications"]}, "patch": {"operationId": "notifications_settings_partial_update", "summary": "Update Notification Preferences", "description": "Update the current user's notification settings.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/NotificationPreferences"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/NotificationPreferences"}}}, "tags": ["notifications"]}, "parameters": []}, "/objectstore/acl/": {"get": {"operationId": "objectstore_acl_list", "summary": "Aggregate ACL view", "description": "Shows, per bucket, which sub-tokens/actions/prefixes apply for the acting user.", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"type": "object", "properties": {"bucket": {"type": "string"}, "grants": {"type": "array", "items": {"type": "object", "properties": {"token_access_key_id": {"type": "string"}, "actions": {"type": "array", "items": {"type": "string"}}, "prefixes": {"type": "array", "items": {"type": "string"}}}}}}}}}}, "tags": ["ObjectStore"]}, "parameters": []}, "/objectstore/buckets/": {"get": {"operationId": "objectstore_buckets_list", "summary": "List buckets", "description": "List S3 buckets available to the acting user (via MasterToken).", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"type": "object", "properties": {"name": {"type": "string"}}}}}}, "tags": ["ObjectStore"]}, "post": {"operationId": "objectstore_buckets_create", "summary": "Create bucket", "description": "Create a new S3 bucket using the acting user's Master Token.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["name"], "type": "object", "properties": {"name": {"type": "string"}, "region": {"type": "string", "default": "auto"}}}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/StorageBucket"}}, "400": {"description": "Bad Request"}}, "tags": ["ObjectStore"]}, "parameters": []}, "/objectstore/buckets/{name}/": {"delete": {"operationId": "objectstore_buckets_delete", "summary": "Delete bucket", "description": "Delete an empty S3 bucket. Only the owner can delete it.", "parameters": [], "responses": {"204": {"description": "Deleted"}, "400": {"description": "Bad Request (e.g. not empty)"}, "404": {"description": "Bucket not found or permission denied"}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "name", "in": "path", "required": true, "type": "string"}]}, "/objectstore/buckets/{name}/acl/": {"get": {"operationId": "objectstore_buckets_acl_list", "summary": "Get bucket ACL (policy)", "description": "Fetch the current S3 bucket policy for the given bucket.", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"bucket": {"type": "string"}, "policy": {"type": "object"}}}}, "400": {"description": "Bad Request"}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "name", "in": "path", "required": true, "type": "string"}]}, "/objectstore/buckets/{name}/acl/set/": {"put": {"operationId": "objectstore_buckets_acl_set_update", "summary": "Set bucket ACL (policy or grants)", "description": "Set the S3 bucket policy either by passing a full policy JSON or by providing grants (actions/prefixes) that will be mapped to a policy.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"type": "object", "properties": {"policy": {"description": "Raw S3 bucket policy (optional)", "type": "object"}, "grants": {"description": "Alternative to policy: list of grants (actions/prefixes) to be mapped to a policy", "type": "array", "items": {"type": "object", "properties": {"actions": {"type": "array", "items": {"type": "string"}}, "prefixes": {"type": "array", "items": {"type": "string"}}}}}}}}], "responses": {"200": {"description": "Updated"}, "400": {"description": "Bad Request"}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "name", "in": "path", "required": true, "type": "string"}]}, "/objectstore/buckets/{name}/objects/": {"get": {"operationId": "objectstore_buckets_objects_list", "summary": "List objects in bucket", "description": "List objects in a specific S3 bucket.", "parameters": [{"name": "prefix", "in": "query", "description": "Prefix to filter objects", "type": "string"}, {"name": "delimiter", "in": "query", "description": "Delimiter for grouping", "type": "string"}, {"name": "max_keys", "in": "query", "description": "Max keys to return", "type": "integer"}, {"name": "continuation_token", "in": "query", "description": "Continuation token for pagination", "type": "string"}, {"name": "sync", "in": "query", "description": "Sync buckets from S3 (deprecated, now default)", "type": "boolean"}], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"Name": {"type": "string"}, "Prefix": {"type": "string"}, "KeyCount": {"type": "integer"}, "MaxKeys": {"type": "integer"}, "IsTruncated": {"type": "boolean"}, "Contents": {"type": "array", "items": {"type": "object", "properties": {"Key": {"type": "string"}, "LastModified": {"type": "string"}, "ETag": {"type": "string"}, "Size": {"type": "integer"}, "StorageClass": {"type": "string"}}}}, "CommonPrefixes": {"type": "array", "items": {"type": "object", "properties": {"Prefix": {"type": "string"}}}}, "NextContinuationToken": {"type": "string"}}}}, "404": {"description": "Bucket not found"}}, "consumes": ["multipart/form-data", "application/x-www-form-urlencoded"], "tags": ["ObjectStore"]}, "post": {"operationId": "objectstore_buckets_objects_create", "summary": "Upload object or create folder", "description": "Upload a file to the bucket or create a folder.", "parameters": [{"name": "key", "in": "formData", "description": "Object key (path)", "required": true, "type": "string"}, {"name": "file", "in": "formData", "description": "File to upload", "type": "file"}], "responses": {"201": {"description": "Created"}, "400": {"description": "Bad Request"}, "404": {"description": "Bucket not found"}}, "consumes": ["multipart/form-data", "application/x-www-form-urlencoded"], "tags": ["ObjectStore"]}, "delete": {"operationId": "objectstore_buckets_objects_delete", "summary": "Delete object", "description": "Delete an object from the bucket.", "parameters": [{"name": "key", "in": "query", "description": "Object key (path) to delete", "required": true, "type": "string"}], "responses": {"204": {"description": "Deleted"}, "400": {"description": "Bad Request"}, "404": {"description": "Bucket not found"}}, "consumes": ["multipart/form-data", "application/x-www-form-urlencoded"], "tags": ["ObjectStore"]}, "parameters": [{"name": "name", "in": "path", "required": true, "type": "string"}]}, "/objectstore/buckets/{name}/presigned-url/": {"get": {"operationId": "objectstore_buckets_presigned-url_list", "summary": "Generate presigned URL", "description": "Generate a presigned URL to temporarily read or write an object in the bucket. Action can be 'get' or 'put'.", "parameters": [{"name": "key", "in": "query", "description": "Object key (path)", "required": true, "type": "string"}, {"name": "action", "in": "query", "description": "Action ('get' or 'put')", "type": "string", "default": "get"}, {"name": "expires_in", "in": "query", "description": "Expiration time in seconds", "type": "integer", "default": 3600}], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"url": {"type": "string"}}}}, "400": {"description": "Bad Request"}, "404": {"description": "Bucket not found or permission denied"}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "name", "in": "path", "required": true, "type": "string"}]}, "/objectstore/master-tokens/": {"get": {"operationId": "objectstore_master-tokens_list", "summary": "List Master Tokens", "description": "List Master Tokens for the acting user.", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/MasterTokenList"}}}}, "tags": ["ObjectStore"]}, "post": {"operationId": "objectstore_master-tokens_create", "summary": "Create Master Token", "description": "Create a new Master Token for the acting user.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/MasterTokenCreate"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/MasterTokenCreateResponse"}}}, "tags": ["ObjectStore"]}, "parameters": []}, "/objectstore/master-tokens/{token_id}/revoke/": {"post": {"operationId": "objectstore_master-tokens_revoke_create", "summary": "Revoke Master Token", "description": "Revoke a Master Token.", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"status": {"type": "string"}}}}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "token_id", "in": "path", "required": true, "type": "string"}]}, "/objectstore/master-tokens/{token_id}/rotate/": {"post": {"operationId": "objectstore_master-tokens_rotate_create", "summary": "Rotate Master Token", "description": "Rotate a Master Token by minting a new secret and revoking the old token.", "parameters": [], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/MasterTokenCreateResponse"}}, "404": {"description": "Not Found"}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "token_id", "in": "path", "required": true, "type": "string"}]}, "/objectstore/sub-tokens/": {"get": {"operationId": "objectstore_sub-tokens_list", "summary": "List Sub Tokens", "description": "List Sub Tokens for the acting user.", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/SubTokenList"}}}}, "tags": ["ObjectStore"]}, "post": {"operationId": "objectstore_sub-tokens_create", "summary": "Create Sub Token", "description": "Create a new Sub Token for the acting user with bucket grants.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/SubTokenCreate"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/SubTokenCreateResponse"}}, "400": {"description": "Bad Request"}, "502": {"description": "Bad Gateway (Gateway Sync Failed)"}}, "tags": ["ObjectStore"]}, "parameters": []}, "/objectstore/sub-tokens/{token_id}/revoke/": {"post": {"operationId": "objectstore_sub-tokens_revoke_create", "summary": "Revoke Sub Token", "description": "Revoke a Sub Token.", "parameters": [], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"status": {"type": "string"}}}}, "404": {"description": "Not Found"}, "502": {"description": "Bad Gateway (Gateway Sync Failed)"}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "token_id", "in": "path", "required": true, "type": "string"}]}, "/objectstore/sub-tokens/{token_id}/rotate/": {"post": {"operationId": "objectstore_sub-tokens_rotate_create", "summary": "Rotate Sub Token", "description": "Rotate a Sub Token by minting a new secret and revoking the old token.", "parameters": [], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/SubTokenCreateResponse"}}, "404": {"description": "Not Found"}, "502": {"description": "Bad Gateway (Gateway Sync Failed)"}}, "tags": ["ObjectStore"]}, "parameters": [{"name": "token_id", "in": "path", "required": true, "type": "string"}]}, "/objectstore/tokens/auth/": {"post": {"operationId": "objectstore_tokens_auth_create", "summary": "Retrieve token secret (encrypted)", "description": "Validator-only: retrieve the secret key for a given accessKeyId, encrypted with the shared OBJECTSTORE_TWEETNACL_KEY using TweetNaCl (SecretBox).", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["accessKeyId"], "type": "object", "properties": {"accessKeyId": {"type": "string"}}}}], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"valid": {"type": "boolean"}, "status": {"type": "string"}, "account_address": {"type": "string"}, "token_type": {"type": "string", "enum": ["master", "sub"]}, "encrypted_secret": {"description": "Base64 encoded TweetNaCl ciphertext (nonce + ciphertext)", "type": "string"}, "nonce": {"description": "Base64 encoded nonce", "type": "string"}}}}, "403": {"description": "Forbidden"}, "400": {"description": "Bad Request"}}, "tags": ["ObjectStore"]}, "parameters": []}, "/objectstore/tokens/verify/": {"post": {"operationId": "objectstore_tokens_verify_create", "summary": "Verify token secret (internal)", "description": "Validator-only: verify an accessKeyId + secret pair via HMAC without revealing secrets.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["accessKeyId", "secret"], "type": "object", "properties": {"accessKeyId": {"type": "string"}, "secret": {"type": "string"}}}}], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"valid": {"type": "boolean"}, "status": {"type": "string"}, "detail": {"type": "string"}, "account_address": {"type": "string"}, "token_type": {"type": "string", "enum": ["master", "sub"]}, "acl": {"description": "Access Control List details for the token", "type": "object", "properties": {"scope": {"description": "Master token scope", "type": "object"}, "owned_buckets": {"description": "Buckets owned by the user (Master Token only)", "type": "array", "items": {"type": "string"}}, "scope_type": {"type": "string", "enum": ["single_bucket", "multi_bucket", "global"]}, "actions": {"type": "array", "items": {"type": "string"}}, "allowed_prefixes": {"type": "array", "items": {"type": "string"}}, "ip_allowlist": {"type": "array", "items": {"type": "string"}}, "grants": {"type": "array", "items": {"type": "object", "properties": {"bucket": {"type": "string"}, "actions": {"type": "array", "items": {"type": "string"}}, "prefixes": {"type": "array", "items": {"type": "string"}}}}}}}}}}, "403": {"description": "Forbidden"}, "400": {"description": "Bad Request"}}, "tags": ["ObjectStore"]}, "parameters": []}, "/registry/events/": {"get": {"operationId": "registry_events_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/health/": {"get": {"operationId": "registry_health_list", "summary": "Lightweight reachability check of the upstream Harbor API.", "description": "Public on purpose so a status page can hit it. Doesn't expose Harbor admin\ncreds — only a boolean + a latency hint.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/keys/": {"get": {"operationId": "registry_keys_list", "summary": "GET = list keys for the active project. POST = create a new key.", "description": "Create body: {name, role, expires_days?}.\nCreate response includes the plaintext secret ONCE; callers must save it.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "post": {"operationId": "registry_keys_create", "summary": "GET = list keys for the active project. POST = create a new key.", "description": "Create body: {name, role, expires_days?}.\nCreate response includes the plaintext secret ONCE; callers must save it.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/keys/{id}/": {"get": {"operationId": "registry_keys_read", "description": "GET = show metadata, DELETE = revoke (deletes Harbor robot + DB row).", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "delete": {"operationId": "registry_keys_delete", "description": "GET = show metadata, DELETE = revoke (deletes Harbor robot + DB row).", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["registry"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/registry/keys/{id}/rotate/": {"post": {"operationId": "registry_keys_rotate_create", "description": "POST = rotate the secret. Returns the new plaintext secret ONCE.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["registry"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/registry/me/": {"get": {"operationId": "registry_me_list", "description": "Get the current user's active Harbor project information.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/me/publicity/": {"patch": {"operationId": "registry_me_publicity_partial_update", "description": "Toggle whether the project is public (anonymous pull) or private.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"type": "object", "properties": {"public": {"type": "boolean"}}}}], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"public": {"type": "boolean"}}}}}, "tags": ["registry"]}, "parameters": []}, "/registry/me/robot/": {"get": {"operationId": "registry_me_robot_list", "description": "Return the robot login (no secret) + a copy-pasteable docker login\ncommand shell. Useful for users who provisioned a while back, lost the\none-shot secret, and want to confirm what login to use after a /rotate.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/namespaces/check/": {"get": {"operationId": "registry_namespaces_check_list", "description": "Check if a Harbor namespace is available.", "parameters": [{"name": "name", "in": "query", "description": "Namespace to check", "type": "string"}], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/plans/": {"get": {"operationId": "registry_plans_list", "description": "Exposes registry pricing plans. Unauthenticated, cached in Redis.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/provision/": {"post": {"operationId": "registry_provision_create", "summary": "Provision a new Harbor project for the user.", "description": "Idempotency rules:\n  - If the user has any ACTIVE projects and is under their plan's max_projects,\n    a new namespace can be created.\n  - If the user already has a project in PROVISIONING state, we return 202\n    with status=\"provisioning\" so the client can poll /provision/status/\n    instead of treating it as a hard error.\n  - If max_projects is reached (counting ACTIVE + PROVISIONING + SUSPENDED),\n    we return 409 telling the user to upgrade or release one.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"type": "object", "properties": {"namespace": {"type": "string"}}}}], "responses": {"201": {"description": "", "schema": {"type": "object", "properties": {"namespace": {"type": "string"}}}}}, "tags": ["registry"]}, "parameters": []}, "/registry/provision/status/": {"get": {"operationId": "registry_provision_status_list", "description": "Lightweight polling endpoint so the frontend can wait for a\nprovisioning project to flip to ACTIVE without retrying POST /provision/.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/repositories/": {"get": {"operationId": "registry_repositories_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/repositories/{repo}/": {"delete": {"operationId": "registry_repositories_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["registry"]}, "parameters": [{"name": "repo", "in": "path", "required": true, "type": "string"}]}, "/registry/repositories/{repo}/artifacts/": {"get": {"operationId": "registry_repositories_artifacts_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": [{"name": "repo", "in": "path", "required": true, "type": "string"}]}, "/registry/repositories/{repo}/artifacts/{reference}/": {"get": {"operationId": "registry_repositories_artifacts_read", "summary": "Get full metadata for one artifact: tags, scan overview, push_time, size.", "description": "`reference` can be either a tag or a digest. Cached 30s to match the\nlist view; busted on push/delete webhook events.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "delete": {"operationId": "registry_repositories_artifacts_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["registry"]}, "parameters": [{"name": "repo", "in": "path", "required": true, "type": "string"}, {"name": "reference", "in": "path", "required": true, "type": "string"}]}, "/registry/robot/rotate/": {"post": {"operationId": "registry_robot_rotate_create", "description": "Rotate the robot secret for the active project.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/subscribe/": {"post": {"operationId": "registry_subscribe_create", "description": "Subscribe the current user to a storage plan.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/subscriptions/": {"get": {"operationId": "registry_subscriptions_list", "description": "List the current user's subscriptions as last seen by the sync task.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/subscriptions/{subscription_id}/": {"delete": {"operationId": "registry_subscriptions_delete", "description": "Cancel one of the current user's subscriptions by its on-chain id.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["registry"]}, "parameters": [{"name": "subscription_id", "in": "path", "required": true, "type": "string"}]}, "/registry/usage/": {"get": {"operationId": "registry_usage_list", "summary": "Overall storage/repo/artifact usage for the active project.", "description": "Live numbers come from Harbor; the 7-day history comes from\nUsageSnapshot rows written by registry.tasks.refresh_usage_snapshots.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/registry/usage/repositories/": {"get": {"operationId": "registry_usage_repositories_list", "description": "Per-repository usage breakdown: list every repo of the user's project\nwith its artifact count, pull count, and rough storage footprint (sum of\nartifact sizes from Harbor's metadata).", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["registry"]}, "parameters": []}, "/ssh-keys/": {"get": {"operationId": "ssh-keys_list", "description": "", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"$ref": "#/definitions/SSHKey"}}}}}}, "tags": ["ssh-keys"]}, "post": {"operationId": "ssh-keys_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/SSHKey"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/SSHKey"}}}, "tags": ["ssh-keys"]}, "parameters": []}, "/ssh-keys/{id}/": {"get": {"operationId": "ssh-keys_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/SSHKey"}}}, "tags": ["ssh-keys"]}, "delete": {"operationId": "ssh-keys_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["ssh-keys"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/storage-control/files/": {"get": {"operationId": "listUserFiles", "description": "List files for the authenticated user, including pinned miner node IDs and miner details. Validators can list files for any user by providing account_ss58.", "parameters": [{"name": "search", "in": "query", "description": "Search by filename (partial) or CID (start).", "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}, {"name": "include_pending", "in": "query", "description": "If true, include files linked to the user that do not yet have a summary (shown as Pending).", "type": "boolean"}, {"name": "account_ss58", "in": "query", "description": "(Validator only) SS58 address of the user to list files for.", "type": "string"}, {"name": "exclude_s3", "in": "query", "description": "If true, exclude files starting with \"s3-\".", "type": "boolean"}, {"name": "only_s3", "in": "query", "description": "If true, only include files starting with \"s3-\".", "type": "boolean"}, {"name": "file_type", "in": "query", "description": "Filter by file extension (e.g., \"pdf\", \"jpg\", \"png\"). Case-insensitive.", "type": "string"}, {"name": "size_min", "in": "query", "description": "Minimum file size in bytes.", "type": "integer"}, {"name": "size_max", "in": "query", "description": "Maximum file size in bytes.", "type": "integer"}, {"name": "date_from", "in": "query", "description": "Filter files created on or after this date (ISO format: YYYY-MM-DD).", "type": "string", "format": "date"}, {"name": "date_to", "in": "query", "description": "Filter files created on or before this date (ISO format: YYYY-MM-DD).", "type": "string", "format": "date"}, {"name": "order_by", "in": "query", "description": "Field to order by: \"date\" (default), \"size\", or \"type\" (file extension).", "type": "string", "enum": ["date", "size", "type"]}, {"name": "order_dir", "in": "query", "description": "Order direction: \"desc\" (default) or \"asc\".", "type": "string", "enum": ["asc", "desc"]}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"$ref": "#/definitions/UserFileSummaryListItem"}}}}}}, "tags": ["Storage Control"]}, "parameters": []}, "/storage-control/files/cid/{cid}/pins/sync/": {"post": {"operationId": "syncFilePinsByCid", "description": "Same as syncFilePins but addressed by CID instead of file_id.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["active"], "type": "object", "properties": {"active": {"type": "object", "properties": {"miner_node_id": {"type": "string"}, "endpoint": {"type": "string", "nullable": true}}}, "remove_missing": {"type": "boolean", "default": true}}}}], "responses": {"201": {"description": "", "schema": {"required": ["active"], "type": "object", "properties": {"active": {"type": "object", "properties": {"miner_node_id": {"type": "string"}, "endpoint": {"type": "string", "nullable": true}}}, "remove_missing": {"type": "boolean", "default": true}}}}}, "tags": ["Storage Control"]}, "parameters": [{"name": "cid", "in": "path", "required": true, "type": "string"}]}, "/storage-control/files/{file_id}/": {"get": {"operationId": "getFileDetail", "description": "Get file details for the authenticated user, including all pinned miners.", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/FileDetail"}}}, "tags": ["Storage Control"]}, "parameters": [{"name": "file_id", "in": "path", "description": "A unique value identifying this file.", "required": true, "type": "string"}]}, "/storage-control/files/{file_id}/pins/sync/": {"post": {"operationId": "syncFilePins", "description": "\nValidator endpoint to synchronize the set of active miner pins for a file.\nProvide the current list of active miners; missing miners will be removed when remove_missing=true.\n", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["active"], "type": "object", "properties": {"active": {"type": "object", "properties": {"miner_node_id": {"type": "string"}, "endpoint": {"type": "string", "nullable": true}}}, "remove_missing": {"type": "boolean", "default": true}}}}], "responses": {"200": {"description": "", "schema": {"type": "object", "properties": {"added": {"type": "integer"}, "updated": {"type": "integer"}, "removed": {"type": "integer"}, "active": {"type": "array", "items": {"type": "string"}}}}}, "400": {"description": "Bad Request"}, "403": {"description": "Forbidden"}, "404": {"description": "Not Found"}}, "tags": ["Storage Control"]}, "parameters": [{"name": "file_id", "in": "path", "required": true, "type": "string"}]}, "/storage-control/health/": {"get": {"operationId": "storage-control_health_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["storage-control"]}, "parameters": []}, "/storage-control/profile-summary/": {"get": {"operationId": "storage-control_profile-summary_list", "summary": "Get user profile summary", "description": "Returns storage usage summary for the authenticated user. Validators/Admins can query other accounts via `account` parameter.", "parameters": [{"name": "account", "in": "query", "description": "Account SS58 (Validator/Admin only)", "type": "string"}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserProfileSummary"}}}, "tags": ["Storage Control"]}, "parameters": []}, "/storage-control/requests/": {"post": {"operationId": "storage-control_requests_create", "description": "Create a user file request.\n\n- request_type values: Upload | Pin | Unpin (default: Pin)\n- Upload registers/updates file metadata; Pin requests pinning; Unpin requests unpin.\n- Regular users act for themselves; account_ss58 is auto-filled from their profile.\n- Validators/Admins (group 'validator' or admin role/scope) may set account_ss58 to act on behalf of a user.\n- Idempotency is handled server-side. Duplicate submits are safely deduped by a stable request_id = account_ss58:cid:request_type.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["cid"], "type": "object", "properties": {"cid": {"description": "Content ID", "type": "string"}, "size_bytes": {"description": "File size in bytes (required for validator/admin calls)", "type": "integer", "minimum": 0}, "original_name": {"description": "Original file name", "type": "string"}, "request_type": {"description": "Request type", "type": "string", "enum": ["Upload", "Pin", "Unpin"], "default": "Pin"}, "account_ss58": {"description": "Optional; validators/admins may set to act on behalf of a user. Otherwise auto-filled from caller's profile.", "type": "string"}}}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/UserFileRequest"}}, "200": {"description": "", "schema": {"$ref": "#/definitions/UserFileRequest"}}}, "tags": ["Storage Control"]}, "parameters": []}, "/storage-control/upload/": {"post": {"operationId": "uploadFile", "description": "Upload a file via multipart/form-data and returns metadata. Requires user to have credits > 0.\n\nValidators/Admins may provide `account_ss58` to upload on behalf of another user.", "parameters": [{"name": "file", "in": "formData", "description": "The file to upload.", "required": true, "type": "file"}, {"name": "account_ss58", "in": "formData", "description": "Optional; validators/admins may set to act on behalf of a user.", "required": false, "type": "string"}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/UserUpload"}}, "400": {"description": "Bad Request"}, "402": {"description": "Insufficient Credits"}, "415": {"description": "Unsupported Media Type"}}, "consumes": ["multipart/form-data"], "tags": ["Storage Control"]}, "parameters": []}, "/storage-control/uploads/": {"get": {"operationId": "listUserUploads", "description": "List uploads for the authenticated user (paginated).", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"$ref": "#/definitions/UserUpload"}}}}}}, "tags": ["Storage Control"]}, "parameters": []}, "/storage-control/uploads/{id}/": {"get": {"operationId": "retrieveUserUpload", "description": "Retrieve a specific upload owned by the authenticated user.", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserUpload"}}}, "tags": ["Storage Control"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/support/analytics/": {"get": {"operationId": "support_analytics_list", "description": "", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"type": "object", "properties": {}}}}}}}, "tags": ["support"]}, "parameters": []}, "/support/tickets/": {"get": {"operationId": "support_tickets_list", "summary": "List tickets", "description": "List tickets for the current user (or all for staff). Supports filters: status, priority, category, search.", "parameters": [{"name": "search", "in": "query", "description": "Search in subject and messages", "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}, {"name": "status", "in": "query", "description": "Filter by status", "type": "string", "enum": ["open", "pending", "resolved", "closed"]}, {"name": "priority", "in": "query", "description": "Filter by priority", "type": "string", "enum": ["low", "normal", "high", "urgent"]}, {"name": "category", "in": "query", "description": "Filter by category", "type": "string"}], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/TicketListItem"}}}}, "tags": ["Support"]}, "post": {"operationId": "support_tickets_create", "summary": "Create ticket", "description": "Create a new support ticket with an initial description.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/TicketCreate"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/TicketDetail"}}}, "tags": ["Support"]}, "parameters": []}, "/support/tickets/{id}/": {"get": {"operationId": "support_tickets_read", "summary": "Get ticket detail", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/TicketDetail"}}}, "tags": ["Support"]}, "put": {"operationId": "support_tickets_update", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/TicketUpdate"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/TicketUpdate"}}}, "tags": ["support"]}, "patch": {"operationId": "support_tickets_partial_update", "summary": "Update ticket (staff fields restricted to staff)", "description": "Users can view their own tickets. Staff can update status, priority, and assignment.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/TicketUpdate"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/TicketDetail"}}}, "tags": ["Support"]}, "parameters": [{"name": "id", "in": "path", "description": "A unique integer value identifying this ticket.", "required": true, "type": "integer"}]}, "/support/tickets/{ticket_id}/messages/": {"get": {"operationId": "support_tickets_messages_list", "summary": "List ticket messages", "description": "Users only see public messages; staff see internal notes too.", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "ordering", "in": "query", "description": "Which field to use when ordering the results.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"type": "array", "items": {"$ref": "#/definitions/TicketMessage"}}}}, "tags": ["Support"]}, "post": {"operationId": "support_tickets_messages_create", "summary": "Post message to ticket", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/TicketMessage"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/TicketMessage"}}}, "tags": ["Support"]}, "parameters": [{"name": "ticket_id", "in": "path", "required": true, "type": "string"}]}, "/support/tickets/{ticket_id}/messages/{message_id}/attachments/": {"post": {"operationId": "support_tickets_messages_attachments_create", "summary": "Upload attachment to a ticket message", "description": "Attach a file (multipart/form-data) to an existing ticket message.", "parameters": [{"name": "file", "in": "formData", "description": "File to attach", "required": true, "type": "file"}, {"name": "filename", "in": "formData", "description": "Optional filename override", "required": false, "type": "string"}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/TicketAttachment"}}, "404": {"description": "Not Found"}, "403": {"description": "Forbidden"}}, "consumes": ["multipart/form-data"], "tags": ["Support"]}, "parameters": [{"name": "ticket_id", "in": "path", "required": true, "type": "string"}, {"name": "message_id", "in": "path", "required": true, "type": "string"}]}, "/user-profile/": {"get": {"operationId": "user-profile_read", "description": "", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/UserProfile"}}}, "tags": ["user-profile"]}, "parameters": []}, "/users/": {"get": {"operationId": "users_list", "description": "ViewSet for viewing and editing user information.", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"$ref": "#/definitions/User"}}}}}}, "tags": ["users"]}, "post": {"operationId": "users_create", "description": "ViewSet for viewing and editing user information.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/User"}}], "responses": {"201": {"description": "", "schema": {"$ref": "#/definitions/User"}}}, "tags": ["users"]}, "parameters": []}, "/users/me/": {"get": {"operationId": "users_me", "description": "ViewSet for viewing and editing user information.", "parameters": [{"name": "search", "in": "query", "description": "A search term.", "required": false, "type": "string"}, {"name": "page", "in": "query", "description": "A page number within the paginated result set.", "required": false, "type": "integer"}, {"name": "page_size", "in": "query", "description": "Number of results to return per page.", "required": false, "type": "integer"}], "responses": {"200": {"description": "", "schema": {"required": ["count", "results"], "type": "object", "properties": {"count": {"type": "integer"}, "next": {"type": "string", "format": "uri", "x-nullable": true}, "previous": {"type": "string", "format": "uri", "x-nullable": true}, "results": {"type": "array", "items": {"$ref": "#/definitions/User"}}}}}}, "tags": ["users"]}, "parameters": []}, "/users/{id}/": {"get": {"operationId": "users_read", "description": "ViewSet for viewing and editing user information.", "parameters": [], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/User"}}}, "tags": ["users"]}, "put": {"operationId": "users_update", "description": "ViewSet for viewing and editing user information.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/User"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/User"}}}, "tags": ["users"]}, "patch": {"operationId": "users_partial_update", "description": "ViewSet for viewing and editing user information.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"$ref": "#/definitions/User"}}], "responses": {"200": {"description": "", "schema": {"$ref": "#/definitions/User"}}}, "tags": ["users"]}, "delete": {"operationId": "users_delete", "description": "ViewSet for viewing and editing user information.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["users"]}, "parameters": [{"name": "id", "in": "path", "required": true, "type": "string"}]}, "/v1/nodes/register": {"post": {"operationId": "registerNode", "description": "Register a new node on the Hippius network. Gas fees are subsidized by API.", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["ss58_address", "node_id", "encryption_key", "challenge_bytes", "signature", "public_key", "wallet_signature"], "type": "object", "properties": {"ss58_address": {"description": "User's Substrate SS58 address", "type": "string"}, "node_id": {"description": "Iroh Node ID (hex)", "type": "string"}, "encryption_key": {"description": "Encryption key (hex)", "type": "string"}, "challenge_bytes": {"description": "Challenge payload bytes (hex)", "type": "string"}, "signature": {"description": "Iroh node signature of challenge (hex)", "type": "string"}, "public_key": {"description": "Iroh public key (hex)", "type": "string"}, "wallet_signature": {"description": "Substrate wallet signature of challenge (hex)", "type": "string"}}}}], "responses": {"202": {"description": "Registration Accepted for Processing", "schema": {"type": "object", "properties": {"status": {"type": "string"}, "message": {"type": "string"}}}, "examples": {"application/json": {"status": "ACCEPTED", "message": "Node registration queued for processing."}}}, "400": {"description": "Transaction Failed / Invalid Data"}, "403": {"description": "Invalid Signature"}, "429": {"description": "Rate Limit Exceeded"}}, "tags": ["Miner"]}, "parameters": []}, "/v1/workspaces/dms/attachments/presign": {"post": {"operationId": "v1_workspaces_dms_attachments_presign_create", "description": "POST /dms/attachments/presign — presigned PUT + GET URLs for a DM\nattachment uploaded to the caller's own per-user bucket.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/attachments/refresh-url": {"post": {"operationId": "v1_workspaces_dms_attachments_refresh-url_create", "description": "POST /dms/attachments/refresh-url — mint a fresh GET presign for a\nDM attachment stored in another user's (or own) per-user bucket.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/blocks": {"get": {"operationId": "v1_workspaces_dms_blocks_list", "description": "GET + POST /dms/blocks — list or create blocks.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_dms_blocks_create", "description": "GET + POST /dms/blocks — list or create blocks.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/blocks/{ss58}": {"delete": {"operationId": "v1_workspaces_dms_blocks_delete", "description": "DELETE /dms/blocks/<ss58> — unblock a user.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ss58", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/dms/envelope-backup/presign": {"get": {"operationId": "v1_workspaces_dms_envelope-backup_presign_list", "summary": "Presigned URLs for DM envelope backup (encrypted binary blobs).", "description": "POST — batch PUT URLs for envelopes, manifest, and ratchet-state.\nGET  — GET URLs for manifest + ratchet-state (restore bootstrap).", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_dms_envelope-backup_presign_create", "summary": "Presigned URLs for DM envelope backup (encrypted binary blobs).", "description": "POST — batch PUT URLs for envelopes, manifest, and ratchet-state.\nGET  — GET URLs for manifest + ratchet-state (restore bootstrap).", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/envelope-backup/restore-urls": {"post": {"operationId": "v1_workspaces_dms_envelope-backup_restore-urls_create", "description": "POST /dms/envelope-backup/restore-urls — batch GET URLs for specific envelope ids.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/envelopes": {"post": {"operationId": "v1_workspaces_dms_envelopes_create", "description": "POST /dms/envelopes — send one encrypted envelope.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/envelopes/inbox": {"get": {"operationId": "v1_workspaces_dms_envelopes_inbox_list", "description": "GET /dms/envelopes — fetch undelivered envelopes for current user.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/plaintext-snapshot/list": {"get": {"operationId": "v1_workspaces_dms_plaintext-snapshot_list_list", "description": "GET /dms/plaintext-snapshot/list?thread_id=X — list snapshot keys under a thread.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/plaintext-snapshot/presign": {"post": {"operationId": "v1_workspaces_dms_plaintext-snapshot_presign_create", "description": "POST /dms/plaintext-snapshot/presign — batch PUT URLs for message snapshots.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/plaintext-snapshot/restore-urls": {"post": {"operationId": "v1_workspaces_dms_plaintext-snapshot_restore-urls_create", "description": "POST /dms/plaintext-snapshot/restore-urls — batch GET URLs for message snapshots.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/threads": {"get": {"operationId": "v1_workspaces_dms_threads_list", "description": "GET /dms/threads — list my DM threads.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_dms_threads_create", "description": "POST /dms/threads — create a DM thread (with block checks).", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/dms/threads/{thread_id}": {"delete": {"operationId": "v1_workspaces_dms_threads_delete", "description": "DELETE /dms/threads/<uuid> — soft-delete own side of a thread.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "thread_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/dms/threads/{thread_id}/typing": {"get": {"operationId": "v1_workspaces_dms_threads_typing_list", "description": "POST/GET /dms/threads/<uuid>/typing — ephemeral typing signal.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_dms_threads_typing_create", "description": "POST/GET /dms/threads/<uuid>/typing — ephemeral typing signal.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "thread_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/internal/push-trigger": {"post": {"operationId": "v1_workspaces_internal_push-trigger_create", "summary": "Trigger Web Push fan-out for a workspace.", "description": "Called by the external Node.js Yjs relay after every broadcast.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/keys/bundle": {"post": {"operationId": "v1_workspaces_keys_bundle_create", "description": "POST /keys/bundle — upload/update own full prekey bundle.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/keys/bundle/{target_ss58}": {"get": {"operationId": "v1_workspaces_keys_bundle_read", "description": "GET /keys/bundle/<ss58> — fetch another user's bundle, atomically popping an OPK.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "target_ss58", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/keys/me/status": {"get": {"operationId": "v1_workspaces_keys_me_status_list", "description": "GET /keys/me/status — own key status for client refill/rotate decisions.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/keys/opks": {"post": {"operationId": "v1_workspaces_keys_opks_create", "description": "POST /keys/opks — refill one-time pre-key batch.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/keys/spk": {"put": {"operationId": "v1_workspaces_keys_spk_update", "description": "PUT /keys/spk — rotate own signed pre-key.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/push/subscribe": {"post": {"operationId": "v1_workspaces_push_subscribe_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/push/subscribe/{sub_id}": {"delete": {"operationId": "v1_workspaces_push_subscribe_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "sub_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/push/subscriptions": {"get": {"operationId": "v1_workspaces_push_subscriptions_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/push/vapid-public-key": {"get": {"operationId": "v1_workspaces_push_vapid-public-key_list", "summary": "Return the VAPID public key so the browser can create a PushSubscription.", "description": "VAPID keys are read from environment variables (not Django settings):\n  VAPID_PUBLIC_KEY  – base64url-encoded\n  VAPID_PRIVATE_KEY – base64url-encoded\n  VAPID_CLAIMS      – JSON, e.g. {\"sub\": \"mailto:admin@kliq3.app\"}", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/shares/{share_token}": {"get": {"operationId": "v1_workspaces_shares_read", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "share_token", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/users/me/public-key": {"get": {"operationId": "v1_workspaces_users_me_public-key_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "put": {"operationId": "v1_workspaces_users_me_public-key_update", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/users/search": {"get": {"operationId": "v1_workspaces_users_search_list", "description": "GET /users/search?q=&limit= — search users by username or ss58 prefix.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": []}, "/v1/workspaces/{ws_id}/member-keys": {"get": {"operationId": "v1_workspaces_member-keys_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/my-envelopes": {"get": {"operationId": "v1_workspaces_my-envelopes_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources": {"get": {"operationId": "v1_workspaces_resources_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_resources_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources/{res_id}": {"get": {"operationId": "v1_workspaces_resources_read", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "delete": {"operationId": "v1_workspaces_resources_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}, {"name": "res_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources/{res_id}/epochs": {"get": {"operationId": "v1_workspaces_resources_epochs_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_resources_epochs_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}, {"name": "res_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources/{res_id}/epochs/{epoch}/envelope": {"get": {"operationId": "v1_workspaces_resources_epochs_envelope_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}, {"name": "res_id", "in": "path", "required": true, "type": "string"}, {"name": "epoch", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources/{res_id}/members": {"get": {"operationId": "v1_workspaces_resources_members_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_resources_members_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}, {"name": "res_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources/{res_id}/members/{user_id}": {"patch": {"operationId": "v1_workspaces_resources_members_partial_update", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "delete": {"operationId": "v1_workspaces_resources_members_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}, {"name": "res_id", "in": "path", "required": true, "type": "string"}, {"name": "user_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources/{res_id}/shares": {"get": {"operationId": "v1_workspaces_resources_shares_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["v1"]}, "post": {"operationId": "v1_workspaces_resources_shares_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}, {"name": "res_id", "in": "path", "required": true, "type": "string"}]}, "/v1/workspaces/{ws_id}/resources/{res_id}/shares/{share_token}": {"delete": {"operationId": "v1_workspaces_resources_shares_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["v1"]}, "parameters": [{"name": "ws_id", "in": "path", "required": true, "type": "string"}, {"name": "res_id", "in": "path", "required": true, "type": "string"}, {"name": "share_token", "in": "path", "required": true, "type": "string"}]}, "/workspaces/": {"get": {"operationId": "workspaces_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "post": {"operationId": "workspaces_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["name"], "type": "object", "properties": {"name": {"description": "Workspace Name", "type": "string"}}}}], "responses": {"201": {"description": "", "schema": {"required": ["name"], "type": "object", "properties": {"name": {"description": "Workspace Name", "type": "string"}}}}}, "tags": ["workspaces"]}, "parameters": []}, "/workspaces/encryption-keys/": {"get": {"operationId": "workspaces_encryption-keys_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "post": {"operationId": "workspaces_encryption-keys_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["pubkey", "encrypted_privkey"], "type": "object", "properties": {"pubkey": {"description": "Public Key Hex", "type": "string"}, "encrypted_privkey": {"description": "Encrypted Private Key Hex", "type": "string"}}}}], "responses": {"201": {"description": "", "schema": {"required": ["pubkey", "encrypted_privkey"], "type": "object", "properties": {"pubkey": {"description": "Public Key Hex", "type": "string"}, "encrypted_privkey": {"description": "Encrypted Private Key Hex", "type": "string"}}}}}, "tags": ["workspaces"]}, "put": {"operationId": "workspaces_encryption-keys_update", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["new_pubkey", "new_encrypted_privkey"], "type": "object", "properties": {"new_pubkey": {"description": "New Public Key Hex", "type": "string"}, "new_encrypted_privkey": {"description": "New Encrypted Private Key Hex", "type": "string"}}}}], "responses": {"200": {"description": "", "schema": {"required": ["new_pubkey", "new_encrypted_privkey"], "type": "object", "properties": {"new_pubkey": {"description": "New Public Key Hex", "type": "string"}, "new_encrypted_privkey": {"description": "New Encrypted Private Key Hex", "type": "string"}}}}}, "tags": ["workspaces"]}, "parameters": []}, "/workspaces/github/oauth/callback/": {"get": {"operationId": "workspaces_github_oauth_callback_list", "summary": "GitHub OAuth redirect landing (single, workspace-independent URL).", "description": "GitHub Apps only allow one fixed callback URL, so this endpoint\nlives at the app root (``/api/workspaces/github/oauth/callback/``)\nand resolves the target workspace from the ``state`` token stashed\nin Redis at ``oauth_start`` time.\n\nThe browser arrives here with no JWT bearer, so this endpoint is\nAllowAny. Auth is enforced by the Redis ``state`` token which is\nsingle-use, 10 min TTL, 32-byte unguessable, and binds\n``(user_id, workspace_id)``.\n\nOn success we 302 back to the frontend path stashed in state,\nadding ``?github=connected&login=<login>``. On failure we 302 back\nwith ``?github=error&reason=<slug>``.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": []}, "/workspaces/me/display-name/": {"get": {"operationId": "workspaces_me_display-name_list", "description": "GET  — current effective display name.\nPATCH — set a new display name.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "patch": {"operationId": "workspaces_me_display-name_partial_update", "description": "GET  — current effective display name.\nPATCH — set a new display name.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": []}, "/workspaces/webhooks/github/": {"post": {"operationId": "workspaces_webhooks_github_create", "summary": "Receive GitHub webhook events.", "description": "Fast ack — side-effects dispatched in a daemon thread.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": []}, "/workspaces/{workspace_id}/": {"get": {"operationId": "workspaces_read", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "delete": {"operationId": "workspaces_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/channels/": {"get": {"operationId": "workspaces_channels_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "post": {"operationId": "workspaces_channels_create", "description": "", "parameters": [{"name": "data", "in": "body", "required": true, "schema": {"required": ["name", "channel_type"], "type": "object", "properties": {"name": {"description": "Channel Name", "type": "string"}, "channel_type": {"type": "string", "enum": ["chat", "document", "board", "folder"]}, "encrypted_keys": {"description": "Map of user pubkeys to encrypted symmetric key", "type": "object"}}}}], "responses": {"201": {"description": "", "schema": {"required": ["name", "channel_type"], "type": "object", "properties": {"name": {"description": "Channel Name", "type": "string"}, "channel_type": {"type": "string", "enum": ["chat", "document", "board", "folder"]}, "encrypted_keys": {"description": "Map of user pubkeys to encrypted symmetric key", "type": "object"}}}}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/channels/{channel_id}/": {"get": {"operationId": "workspaces_channels_read", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "delete": {"operationId": "workspaces_channels_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "channel_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/channels/{channel_id}/keys/": {"put": {"operationId": "workspaces_channels_keys_update", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "channel_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/audit-log/": {"get": {"operationId": "workspaces_github_audit-log_list", "description": "Return the GitHub audit log for a workspace.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/events/": {"get": {"operationId": "workspaces_github_events_list", "summary": "Return sealed webhook events since a cursor (auto-incrementing ID).", "description": "Query params:\n    since — integer ID cursor (exclusive). Defaults to 0.\n    limit — max results. Defaults to 50.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/install-callback/": {"post": {"operationId": "workspaces_github_install-callback_create", "summary": "Record a new GitHub App installation after the user completes OAuth flow.", "description": "The client sends ``{ installation_id }`` obtained from the GitHub\nredirect callback.  We fetch installation metadata + accessible\nrepositories from GitHub so the UI can immediately list repos to\nlink without a second round-trip.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/installations/": {"get": {"operationId": "workspaces_github_installations_list", "description": "List GitHub App installations accessible to the current user.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/issues/": {"get": {"operationId": "workspaces_github_issues_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "post": {"operationId": "workspaces_github_issues_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/issues/{binding_id}/": {"patch": {"operationId": "workspaces_github_issues_partial_update", "description": "Close, reopen, or comment on a bound GitHub issue.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "binding_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/issues/{number}/comment/": {"post": {"operationId": "workspaces_github_issues_comment_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/issues/{number}/detail/": {"get": {"operationId": "workspaces_github_issues_detail_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/issues/{number}/state/": {"post": {"operationId": "workspaces_github_issues_state_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/oauth/disconnect/": {"post": {"operationId": "workspaces_github_oauth_disconnect_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/oauth/me/": {"get": {"operationId": "workspaces_github_oauth_me_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/oauth/start/": {"get": {"operationId": "workspaces_github_oauth_start_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/pulls/": {"get": {"operationId": "workspaces_github_pulls_list", "summary": "List pull requests across this workspace's linked repos.", "description": "Query params:\n  - ``owner`` + ``repo`` (optional): limit to one repo\n  - ``state`` (optional, default ``open``): ``open`` / ``closed`` / ``all``\n  - ``q`` (optional): client-side fulltext over title/body/branch", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/pulls/{number}/comment/": {"post": {"operationId": "workspaces_github_pulls_comment_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/pulls/{number}/detail/": {"get": {"operationId": "workspaces_github_pulls_detail_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/pulls/{number}/merge/": {"post": {"operationId": "workspaces_github_pulls_merge_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/pulls/{number}/review/": {"post": {"operationId": "workspaces_github_pulls_review_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/pulls/{number}/state/": {"post": {"operationId": "workspaces_github_pulls_state_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "number", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/repos/": {"get": {"operationId": "workspaces_github_repos_list", "summary": "List (GET, any member) or create (POST, admin only) repo links.", "description": "The GET handler was missing entirely before; clients calling\n``listRepoLinks`` silently got a 405 swallowed by the frontend's\ntry/catch, so ``LINKED REPOS (0)`` was shown even after a\nsuccessful link.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "post": {"operationId": "workspaces_github_repos_create", "summary": "List (GET, any member) or create (POST, admin only) repo links.", "description": "The GET handler was missing entirely before; clients calling\n``listRepoLinks`` silently got a 405 swallowed by the frontend's\ntry/catch, so ``LINKED REPOS (0)`` was shown even after a\nsuccessful link.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/repos/{link_id}/": {"delete": {"operationId": "workspaces_github_repos_delete", "description": "Unlink a GitHub repository from this workspace.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "link_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/webhook-keys/": {"post": {"operationId": "workspaces_github_webhook-keys_create", "summary": "Set the workspace webhook pubkey and store sealed privkeys for members.", "description": "The client generates the x25519 keypair and seals the private key\nfor each member using their x25519 public key.  The backend only\nstores the opaque sealed blobs.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/webhook-keys/members/": {"post": {"operationId": "workspaces_github_webhook-keys_members_create", "description": "Add a sealed webhook privkey for a new workspace member.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/workflow-runs/": {"get": {"operationId": "workspaces_github_workflow-runs_list", "description": "List recent workflow runs for a linked repo.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/github/workflow-runs/dispatch/": {"post": {"operationId": "workspaces_github_workflow-runs_dispatch_create", "description": "Dispatch a workflow run.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/join/": {"post": {"operationId": "workspaces_join_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/members/": {"get": {"operationId": "workspaces_members_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "post": {"operationId": "workspaces_members_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/members/{ss58_address}/": {"put": {"operationId": "workspaces_members_update", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "delete": {"operationId": "workspaces_members_delete", "description": "", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "ss58_address", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/notifications/settings/": {"get": {"operationId": "workspaces_notifications_settings_list", "description": "GET returns (or auto-creates) notification settings for the\nauthenticated user in this workspace.  PATCH partially updates them.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "patch": {"operationId": "workspaces_notifications_settings_partial_update", "description": "GET returns (or auto-creates) notification settings for the\nauthenticated user in this workspace.  PATCH partially updates them.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/objects/": {"get": {"operationId": "workspaces_objects_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/pending-rewraps/": {"get": {"operationId": "workspaces_pending-rewraps_list", "description": "", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/pending-rewraps/{epoch_id}/claim/": {"post": {"operationId": "workspaces_pending-rewraps_claim_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "epoch_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/pending-rewraps/{epoch_id}/complete/": {"post": {"operationId": "workspaces_pending-rewraps_complete_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "epoch_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/presign/": {"post": {"operationId": "workspaces_presign_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/presign/batch/": {"post": {"operationId": "workspaces_presign_batch_create", "description": "", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/projects/": {"get": {"operationId": "workspaces_projects_list", "description": "List projects the user can see, or create a new project.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "post": {"operationId": "workspaces_projects_create", "description": "List projects the user can see, or create a new project.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/projects/{project_id}/": {"patch": {"operationId": "workspaces_projects_partial_update", "description": "Update or delete a project.", "parameters": [], "responses": {"200": {"description": ""}}, "tags": ["workspaces"]}, "delete": {"operationId": "workspaces_projects_delete", "description": "Update or delete a project.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "project_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/projects/{project_id}/channels/{channel_id}/": {"post": {"operationId": "workspaces_projects_channels_create", "description": "Attach or detach a channel from a project.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "delete": {"operationId": "workspaces_projects_channels_delete", "description": "Attach or detach a channel from a project.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "project_id", "in": "path", "required": true, "type": "string"}, {"name": "channel_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/projects/{project_id}/members/": {"post": {"operationId": "workspaces_projects_members_create", "summary": "Add (or update) a member on a project.", "description": "Requires project admin. The target user must already be a workspace member.", "parameters": [], "responses": {"201": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "project_id", "in": "path", "required": true, "type": "string"}]}, "/workspaces/{workspace_id}/projects/{project_id}/members/{user_id}/": {"delete": {"operationId": "workspaces_projects_members_delete", "summary": "Remove a member from a project.", "description": "Requires project admin. Refuses if it would leave zero admins.", "parameters": [], "responses": {"204": {"description": ""}}, "tags": ["workspaces"]}, "parameters": [{"name": "workspace_id", "in": "path", "required": true, "type": "string"}, {"name": "project_id", "in": "path", "required": true, "type": "string"}, {"name": "user_id", "in": "path", "required": true, "type": "string"}]}}, "definitions": {"AuthCodeExchange": {"required": ["code"], "type": "object", "properties": {"code": {"title": "Code", "type": "string", "maxLength": 128, "minLength": 1}, "code_verifier": {"title": "Code verifier", "type": "string", "maxLength": 256}}}, "TaoPrice": {"type": "object", "properties": {"price_usd": {"title": "Price usd", "description": "TAO price in USD", "type": "string", "format": "decimal", "readOnly": true}, "market_cap": {"title": "Market cap", "description": "Market capitalization in USD", "type": "string", "format": "decimal", "readOnly": true, "x-nullable": true}, "volume_24h": {"title": "Volume 24h", "description": "24-hour trading volume in USD", "type": "string", "format": "decimal", "readOnly": true, "x-nullable": true}, "percent_change_24h": {"title": "Percent change 24h", "description": "24-hour price change percentage", "type": "string", "format": "decimal", "readOnly": true, "x-nullable": true}, "circulating_supply": {"title": "Circulating supply", "description": "Current circulating supply of TAO", "type": "string", "format": "decimal", "readOnly": true, "x-nullable": true}, "last_updated": {"title": "Last updated", "description": "Timestamp from the API when the price was last updated", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}}}, "SubscriptionModel": {"type": "object", "properties": {"id": {"title": "ID", "type": "integer", "readOnly": true}, "user": {"title": "User", "type": "integer", "readOnly": true}, "user_email": {"title": "User email", "type": "string", "format": "email", "readOnly": true, "minLength": 1}, "stripe_subscription_id": {"title": "Stripe subscription id", "type": "string", "readOnly": true, "minLength": 1}, "status": {"title": "Status", "type": "string", "enum": ["active", "past_due", "unpaid", "canceled", "incomplete", "incomplete_expired", "trialing", "paused"], "readOnly": true}, "price_id": {"title": "Price id", "type": "string", "readOnly": true, "minLength": 1}, "current_period_start": {"title": "Current period start", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}, "current_period_end": {"title": "Current period end", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}, "cancel_at_period_end": {"title": "Cancel at period end", "type": "boolean", "readOnly": true}, "canceled_at": {"title": "Canceled at", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}, "cancellation_reason": {"title": "Cancellation reason", "description": "Reason for cancellation provided by user or Stripe", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}}}, "SubstrateAddress": {"type": "object", "properties": {"ss58_address": {"title": "Ss58 address", "type": "string", "readOnly": true, "minLength": 1}}, "title": "Substrate Address", "description": "A user's substrate address for receiving payments"}, "NotificationPreferences": {"type": "object", "properties": {"email_enabled": {"title": "Email enabled", "description": "Enable/disable all email notifications", "type": "boolean"}, "low_credit_alerts": {"title": "Low credit alerts", "description": "Receive low credit warnings", "type": "boolean"}, "zero_balance_alerts": {"title": "Zero balance alerts", "description": "Receive zero balance/suspension alerts", "type": "boolean"}, "file_status_updates": {"title": "File status updates", "description": "Receive updates when files are pinned/failed", "type": "boolean"}, "marketing_emails": {"title": "Marketing emails", "description": "Receive marketing and feature updates", "type": "boolean"}}}, "StorageBucket": {"required": ["name"], "type": "object", "properties": {"id": {"title": "Id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 255, "minLength": 1}, "region": {"title": "Region", "type": "string", "maxLength": 64, "minLength": 1}, "is_managed": {"title": "Is managed", "type": "boolean"}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}}}, "MasterTokenList": {"required": ["access_key_id"], "type": "object", "properties": {"id": {"title": "Id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 128}, "access_key_id": {"title": "Access key id", "type": "string", "maxLength": 64, "minLength": 1}, "status": {"title": "Status", "type": "string", "enum": ["active", "suspended", "revoked", "expired"]}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "rotated_at": {"title": "Rotated at", "type": "string", "format": "date-time", "x-nullable": true}, "expires_at": {"title": "Expires at", "type": "string", "format": "date-time", "x-nullable": true}, "last_used_at": {"title": "Last used at", "type": "string", "format": "date-time", "x-nullable": true}, "last4": {"title": "Last4", "type": "string", "readOnly": true}}}, "MasterTokenCreate": {"type": "object", "properties": {"name": {"title": "Name", "type": "string", "default": ""}, "expires_at": {"title": "Expires at", "type": "string", "format": "date-time", "x-nullable": true}}}, "MasterTokenCreateResponse": {"required": ["accessKeyId", "secret", "id", "name", "status", "createdAt", "expiresAt"], "type": "object", "properties": {"accessKeyId": {"title": "Accesskeyid", "type": "string", "minLength": 1}, "secret": {"title": "Secret", "type": "string", "minLength": 1}, "id": {"title": "Id", "type": "string", "format": "uuid"}, "name": {"title": "Name", "type": "string", "minLength": 1}, "status": {"title": "Status", "type": "string", "minLength": 1}, "createdAt": {"title": "Createdat", "type": "string", "format": "date-time"}, "expiresAt": {"title": "Expiresat", "type": "string", "format": "date-time", "x-nullable": true}}}, "SubTokenList": {"required": ["access_key_id"], "type": "object", "properties": {"id": {"title": "Id", "type": "string", "format": "uuid", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 128}, "access_key_id": {"title": "Access key id", "type": "string", "maxLength": 64, "minLength": 1}, "status": {"title": "Status", "type": "string", "enum": ["active", "suspended", "revoked", "expired"]}, "scope_type": {"title": "Scope type", "type": "string", "enum": ["single_bucket", "multi_bucket", "global"]}, "actions": {"title": "Actions", "type": "object"}, "allowed_prefixes": {"title": "Allowed prefixes", "type": "object"}, "ip_allowlist": {"title": "Ip allowlist", "type": "object"}, "not_before": {"title": "Not before", "type": "string", "format": "date-time", "x-nullable": true}, "expires_at": {"title": "Expires at", "type": "string", "format": "date-time", "x-nullable": true}, "last_used_at": {"title": "Last used at", "type": "string", "format": "date-time", "x-nullable": true}, "buckets": {"type": "array", "items": {"$ref": "#/definitions/StorageBucket"}, "readOnly": true}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "last4": {"title": "Last4", "type": "string", "readOnly": true}}}, "SubTokenCreate": {"type": "object", "properties": {"name": {"title": "Name", "type": "string", "default": ""}, "scope_type": {"title": "Scope type", "type": "string", "enum": ["single_bucket", "multi_bucket", "global"], "default": "single_bucket"}, "bucket_names": {"type": "array", "items": {"type": "string", "minLength": 1}, "default": []}, "actions": {"type": "array", "items": {"type": "string", "minLength": 1}, "default": []}, "allowed_prefixes": {"type": "array", "items": {"type": "string", "minLength": 1}, "default": []}, "ip_allowlist": {"type": "array", "items": {"type": "string", "minLength": 1}, "default": []}, "not_before": {"title": "Not before", "type": "string", "format": "date-time", "x-nullable": true}, "expires_at": {"title": "Expires at", "type": "string", "format": "date-time", "x-nullable": true}}}, "SubTokenCreateResponse": {"required": ["accessKeyId", "secret", "id", "name", "status", "createdAt", "expiresAt"], "type": "object", "properties": {"accessKeyId": {"title": "Accesskeyid", "type": "string", "minLength": 1}, "secret": {"title": "Secret", "type": "string", "minLength": 1}, "id": {"title": "Id", "type": "string", "format": "uuid"}, "name": {"title": "Name", "type": "string", "minLength": 1}, "status": {"title": "Status", "type": "string", "minLength": 1}, "createdAt": {"title": "Createdat", "type": "string", "format": "date-time"}, "expiresAt": {"title": "Expiresat", "type": "string", "format": "date-time", "x-nullable": true}}}, "SSHKey": {"required": ["name", "public_key"], "type": "object", "properties": {"id": {"title": "ID", "type": "integer", "readOnly": true}, "name": {"title": "Name", "type": "string", "maxLength": 100, "minLength": 1}, "public_key": {"title": "Public key", "type": "string", "minLength": 1}, "fingerprint": {"title": "Fingerprint", "type": "string", "readOnly": true, "minLength": 1}, "created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "last_used": {"title": "Last used", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}}}, "UserFileSummaryListItem": {"type": "object", "properties": {"file_id": {"title": "File id", "type": "string", "readOnly": true}, "cid": {"title": "Cid", "type": "string", "readOnly": true, "minLength": 1}, "original_name": {"title": "Original name", "type": "string", "readOnly": true}, "size_bytes": {"title": "Size bytes", "type": "integer", "readOnly": true}, "status": {"title": "Status", "type": "string", "enum": ["Pending", "VerifiedLight", "Pinned", "Assigned", "Ingested", "Failed", "PendingDelete"], "readOnly": true}, "pinned_node_ids": {"type": "array", "items": {"title": "Pinned node ids", "type": "string", "maxLength": 128, "minLength": 1}, "readOnly": true}, "active_replica_count": {"title": "Active replica count", "type": "integer", "readOnly": true}, "miners": {"title": "Miners", "type": "string", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}, "created_at": {"title": "Created at", "type": "string", "readOnly": true}}}, "FileDetail": {"type": "object", "properties": {"file_id": {"title": "File id", "type": "string", "readOnly": true, "minLength": 1}, "cid": {"title": "Cid", "type": "string", "readOnly": true, "minLength": 1}, "original_name": {"title": "Original name", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "size_bytes": {"title": "Size bytes", "type": "integer", "readOnly": true}, "status": {"title": "Status", "type": "string", "enum": ["Pending", "VerifiedLight", "Pinned", "Assigned", "Ingested", "Failed", "PendingDelete"], "readOnly": true}, "pinned_node_ids": {"title": "Pinned node ids", "type": "string", "readOnly": true}, "miners": {"title": "Miners", "type": "string", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}}}, "UserProfileSummary": {"type": "object", "properties": {"account_ss58": {"title": "Account ss58", "type": "string", "readOnly": true, "minLength": 1}, "total_files": {"title": "Total files", "type": "integer", "readOnly": true}, "assigned_bytes": {"title": "Assigned bytes", "type": "integer", "readOnly": true}, "active_replicas": {"title": "Active replicas", "type": "integer", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}}}, "UserFileRequest": {"required": ["user", "account_ss58", "cid", "request_type"], "type": "object", "properties": {"id": {"title": "Id", "type": "string", "format": "uuid", "readOnly": true}, "request_id": {"title": "Request id", "type": "string", "readOnly": true, "minLength": 1}, "user": {"title": "User", "type": "integer"}, "account_ss58": {"title": "Account ss58", "type": "string", "maxLength": 64, "minLength": 1}, "cid": {"title": "Cid", "type": "string", "maxLength": 128, "minLength": 1}, "file_id": {"title": "File id", "type": "string", "maxLength": 64, "x-nullable": true}, "original_name": {"title": "Original name", "type": "string", "maxLength": 512, "x-nullable": true}, "request_type": {"title": "Request type", "type": "string", "enum": ["Upload", "Pin", "Unpin"]}, "status": {"title": "Status", "type": "string", "enum": ["Pending", "Processing", "Completed", "Failed", "InsufficientCredits", "Rejected"], "readOnly": true}, "posted_by_vali": {"title": "Posted by vali", "type": "boolean", "readOnly": true}, "last_error": {"title": "Last error", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "published_at": {"title": "Published at", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}, "completed_at": {"title": "Completed at", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}}}, "UserUpload": {"type": "object", "properties": {"id": {"title": "Id", "type": "string", "format": "uuid", "readOnly": true}, "original_name": {"title": "Original name", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "content_type": {"title": "Content type", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "size_bytes": {"title": "Size bytes", "type": "integer", "readOnly": true}, "sha256_hex": {"title": "Sha256 hex", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "cid": {"title": "Cid", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "status": {"title": "Status", "type": "string", "readOnly": true, "minLength": 1}, "file_url": {"title": "File url", "type": "string", "readOnly": true}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}}}, "TicketListItem": {"required": ["subject"], "type": "object", "properties": {"id": {"title": "ID", "type": "integer", "readOnly": true}, "subject": {"title": "Subject", "type": "string", "maxLength": 255, "minLength": 1}, "status": {"title": "Status", "type": "string", "enum": ["open", "pending", "resolved", "closed"]}, "priority": {"title": "Priority", "type": "string", "enum": ["low", "normal", "high", "urgent"]}, "category": {"title": "Category", "type": "string", "maxLength": 50}, "resource_type": {"title": "Resource type", "type": "string", "maxLength": 50}, "resource_id": {"title": "Resource id", "type": "string", "maxLength": 255}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}, "last_user_reply_at": {"title": "Last user reply at", "type": "string", "format": "date-time", "x-nullable": true}, "last_staff_reply_at": {"title": "Last staff reply at", "type": "string", "format": "date-time", "x-nullable": true}, "last_message_at": {"title": "Last message at", "type": "string", "readOnly": true}}}, "TicketCreate": {"required": ["subject", "description"], "type": "object", "properties": {"subject": {"title": "Subject", "type": "string", "maxLength": 255, "minLength": 1}, "priority": {"title": "Priority", "type": "string", "enum": ["low", "normal", "high", "urgent"]}, "category": {"title": "Category", "type": "string", "maxLength": 50}, "resource_type": {"title": "Resource type", "type": "string", "maxLength": 50}, "resource_id": {"title": "Resource id", "type": "string", "maxLength": 255}, "description": {"title": "Description", "type": "string", "minLength": 1}}}, "TicketDetail": {"required": ["subject"], "type": "object", "properties": {"id": {"title": "ID", "type": "integer", "readOnly": true}, "subject": {"title": "Subject", "type": "string", "maxLength": 255, "minLength": 1}, "status": {"title": "Status", "type": "string", "enum": ["open", "pending", "resolved", "closed"]}, "priority": {"title": "Priority", "type": "string", "enum": ["low", "normal", "high", "urgent"]}, "category": {"title": "Category", "type": "string", "maxLength": 50}, "resource_type": {"title": "Resource type", "type": "string", "maxLength": 50}, "resource_id": {"title": "Resource id", "type": "string", "maxLength": 255}, "created_by": {"title": "Created by", "type": "integer", "readOnly": true}, "creator_info": {"title": "Creator info", "type": "string", "readOnly": true}, "assigned_to": {"title": "Assigned to", "type": "integer", "x-nullable": true}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "updated_at": {"title": "Updated at", "type": "string", "format": "date-time", "readOnly": true}, "closed_at": {"title": "Closed at", "type": "string", "format": "date-time", "readOnly": true, "x-nullable": true}, "messages": {"title": "Messages", "type": "string", "readOnly": true}}}, "TicketUpdate": {"type": "object", "properties": {"status": {"title": "Status", "type": "string", "enum": ["open", "pending", "resolved", "closed"]}, "priority": {"title": "Priority", "type": "string", "enum": ["low", "normal", "high", "urgent"]}, "assigned_to": {"title": "Assigned to", "type": "integer", "x-nullable": true}}}, "TicketAttachment": {"type": "object", "properties": {"id": {"title": "ID", "type": "integer", "readOnly": true}, "filename": {"title": "Filename", "type": "string", "maxLength": 255}, "file": {"title": "File", "type": "string", "readOnly": true, "format": "uri"}, "uploaded_at": {"title": "Uploaded at", "type": "string", "format": "date-time", "readOnly": true}}}, "TicketMessage": {"required": ["body"], "type": "object", "properties": {"id": {"title": "ID", "type": "integer", "readOnly": true}, "author": {"title": "Author", "type": "integer", "readOnly": true, "x-nullable": true}, "author_is_staff": {"title": "Author is staff", "type": "boolean", "readOnly": true}, "author_is_requester": {"title": "Author is requester", "type": "boolean", "readOnly": true}, "author_display": {"title": "Author display", "type": "string", "readOnly": true}, "message_type": {"title": "Message type", "type": "string", "enum": ["public", "internal"]}, "body": {"title": "Body", "type": "string", "minLength": 1}, "created_at": {"title": "Created at", "type": "string", "format": "date-time", "readOnly": true}, "attachments": {"type": "array", "items": {"$ref": "#/definitions/TicketAttachment"}, "readOnly": true}}}, "UserProfile": {"type": "object", "properties": {"created": {"title": "Created", "type": "string", "format": "date-time", "readOnly": true}, "user": {"title": "User", "type": "integer", "readOnly": true}, "evm_address": {"title": "Evm address", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "substrate_address": {"title": "Substrate address", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "wallet_verified": {"title": "Wallet verified", "type": "boolean", "readOnly": true}, "neurons": {"title": "Neurons", "type": "integer", "readOnly": true}, "coin_balance": {"title": "Coin balance", "type": "integer", "readOnly": true}, "avatar": {"title": "Avatar", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}, "referral_code": {"title": "Referral code", "description": "Code of the user who referred this user", "type": "string", "readOnly": true, "minLength": 1, "x-nullable": true}}}, "User": {"required": ["username"], "type": "object", "properties": {"id": {"title": "ID", "type": "integer", "readOnly": true}, "username": {"title": "Username", "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "type": "string", "pattern": "^[\\w.@+-]+$", "maxLength": 150, "minLength": 1}, "email": {"title": "Email address", "type": "string", "format": "email", "maxLength": 254}, "first_name": {"title": "First name", "type": "string", "maxLength": 150}, "last_name": {"title": "Last name", "type": "string", "maxLength": 150}, "is_staff": {"title": "Staff status", "description": "Designates whether the user can log into this admin site.", "type": "boolean", "readOnly": true}, "is_superuser": {"title": "Superuser status", "description": "Designates that this user has all permissions without explicitly assigning them.", "type": "boolean", "readOnly": true}, "date_joined": {"title": "Date joined", "type": "string", "format": "date-time", "readOnly": true}}}}}