{
  "openapi": "3.0.3",
  "info": {
    "title": "Vox partner VPN API",
    "version": "1.1.0",
    "description": "White-label VPN API: create users, fetch WireGuard / AmneziaWG configs, list locations and usage. Human-readable docs: https://vox-vpn.com/partner-api",
    "contact": {
      "name": "Vox partner team",
      "url": "https://vox-vpn.com/white-label-vpn"
    }
  },
  "servers": [
    {
      "url": "https://api.vox-vpn.com/partner/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Meta"
    },
    {
      "name": "Locations"
    },
    {
      "name": "Users"
    },
    {
      "name": "Configs"
    },
    {
      "name": "Usage"
    }
  ],
  "paths": {
    "/changelog": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Release notes (no key needed)",
        "operationId": "getChangelog",
        "security": [],
        "responses": {
          "200": {
            "description": "Changelog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    },
                    "changelog": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Release"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/locations": {
      "get": {
        "tags": [
          "Locations"
        ],
        "summary": "List locations available to your account",
        "operationId": "listLocations",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "ISO 3166-1 alpha-2, comma-separated (e.g. JP,KR)."
          },
          {
            "name": "continent",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "asia, europe, north-america, south-america, oceania, middle-east, africa (comma-separated)."
          },
          {
            "name": "protocol",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "wireguard",
                "amneziawg"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Text search in name, ID and country code."
          }
        ],
        "responses": {
          "200": {
            "description": "Locations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "locations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Location"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/users": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "List users",
        "operationId": "listUsers",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "suspended"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Create a user (idempotent on external_id)",
        "operationId": "createUser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "external_id": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_.:@+\\-]{1,80}$"
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User created or already existing",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/User"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "invalid_external_id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_limit_reached / partner_suspended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/users/{external_id}": {
      "parameters": [
        {
          "name": "external_id",
          "in": "path",
          "required": true,
          "description": "Your ID for the user (URL-encoded).",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.:@+\\-]{1,80}$"
          }
        }
      ],
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Get a user",
        "operationId": "getUser",
        "responses": {
          "200": {
            "description": "User",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/User"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "account_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Users"
        ],
        "summary": "Delete a user and its keys",
        "operationId": "deleteUser",
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "account_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/users/{external_id}/suspend": {
      "parameters": [
        {
          "name": "external_id",
          "in": "path",
          "required": true,
          "description": "Your ID for the user (URL-encoded).",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.:@+\\-]{1,80}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Suspend (disconnect and block)",
        "operationId": "suspendUser",
        "responses": {
          "200": {
            "description": "User",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/User"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "account_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/users/{external_id}/resume": {
      "parameters": [
        {
          "name": "external_id",
          "in": "path",
          "required": true,
          "description": "Your ID for the user (URL-encoded).",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.:@+\\-]{1,80}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Resume a suspended user",
        "operationId": "resumeUser",
        "responses": {
          "200": {
            "description": "User",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/User"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "account_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/users/{external_id}/config": {
      "parameters": [
        {
          "name": "external_id",
          "in": "path",
          "required": true,
          "description": "Your ID for the user (URL-encoded).",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.:@+\\-]{1,80}$"
          }
        }
      ],
      "get": {
        "tags": [
          "Configs"
        ],
        "summary": "Get a connection config",
        "operationId": "getConfig",
        "description": "Fetch a fresh config right before each connection: server IPs can change when blocked. Limited to 120 requests/minute per partner.",
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "protocol",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "amneziawg",
                "wireguard"
              ],
              "default": "amneziawg"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "conf"
              ],
              "default": "json"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Config (JSON, or text/plain when format=conf)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Config"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "bad_protocol / protocol_unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "account_not_found / location_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "tags": [
          "Usage"
        ],
        "summary": "Account usage",
        "operationId": "getUsage",
        "responses": {
          "200": {
            "description": "Usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "API version and key check",
        "operationId": "getRoot",
        "responses": {
          "200": {
            "description": "Version info",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "api": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "partner_id": {
                      "type": "integer"
                    },
                    "docs": {
                      "type": "string"
                    },
                    "changelog": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Release"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or revoked API key (invalid_api_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable — retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from the partner dashboard (vxk_…)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "detail": {
            "type": "string",
            "example": "account_not_found"
          }
        }
      },
      "Release": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "changes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Location": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "hk"
          },
          "label": {
            "type": "string",
            "example": "Hong Kong"
          },
          "country": {
            "type": "string",
            "example": "HK"
          },
          "country_name": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "continent": {
            "type": "string",
            "example": "asia"
          },
          "protocols": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "wireguard",
                "amneziawg"
              ]
            }
          }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "external_id": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "suspended"
            ]
          },
          "online": {
            "type": "boolean"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_location": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_config_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Config": {
        "type": "object",
        "properties": {
          "account_id": {
            "type": "integer"
          },
          "external_id": {
            "type": "string"
          },
          "location": {
            "type": "string"
          },
          "location_label": {
            "type": "string"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "amneziawg",
              "wireguard"
            ]
          },
          "endpoint": {
            "type": "string",
            "example": "203.0.113.10:443"
          },
          "config": {
            "type": "string",
            "description": "wg-quick style .conf text (contains the user's private key)."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "Usage": {
        "type": "object",
        "properties": {
          "accounts": {
            "type": "integer"
          },
          "active_accounts": {
            "type": "integer"
          },
          "suspended_accounts": {
            "type": "integer"
          },
          "max_accounts": {
            "type": "integer"
          },
          "online_now": {
            "type": "integer"
          },
          "active_24h": {
            "type": "integer"
          },
          "billable_this_month": {
            "type": "integer"
          },
          "month": {
            "type": "string",
            "example": "2026-10"
          },
          "locations_24h": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "location": {
                  "type": "string"
                },
                "accounts": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    }
  }
}