{
  "openapi": "3.0.3",
  "info": {
    "title": "Voidly VPN availability API",
    "version": "1.0.0",
    "description": "A public, read-only service-to-node control-health observation. It does not verify a device tunnel, routing protection, or configuration issuance. See https://voidly.ai/vpn/agents for the human guide."
  },
  "servers": [
    {
      "url": "https://api.voidly.ai",
      "description": "Production API"
    }
  ],
  "tags": [
    {
      "name": "VPN Availability",
      "description": "Read-only service-to-node control health; not device-tunnel or issuance proof"
    }
  ],
  "paths": {
    "/v1/vpn/availability": {
      "get": {
        "operationId": "getVpnAvailability",
        "summary": "Read VPN region control health",
        "description": "Public read-only GET; no account or API key. Budgeted at 60 requests per minute per source network (IPv4 address or IPv6 /64). Returns a timestamped, short-lived Worker-to-node control-health observation. The general list omits held region IDs; an exact query for a held configured ID returns an unavailable row. A passing result does not verify reachability from a user's network, a WireGuard tunnel, routing protection, or configuration issuance. No IP, peer count, uptime, or capacity data is returned. Parse HTTP status and headers separately from JSON; non-200 responses are not availability verdicts.",
        "tags": [
          "VPN Availability"
        ],
        "security": [],
        "parameters": [
          {
            "name": "region",
            "in": "query",
            "required": false,
            "description": "One exact configured region ID. Omit to read all offered regions.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,128}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounded service-to-node control-health observation. Calculate freshness from observed_at and fresh_for_seconds. Repeated reads can reuse an in-service 15-second snapshot with its original observation time; HTTP responses are no-store.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Read budget for this source network: 60 requests per minute.",
                "schema": {
                  "type": "integer",
                  "enum": [
                    60
                  ]
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Limiter-reported remaining reads for this source network.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Limiter reset time, Unix seconds.",
                "schema": {
                  "type": "integer",
                  "format": "int64"
                }
              },
              "Cache-Control": {
                "description": "HTTP response is not cacheable.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "schema",
                    "observed_at",
                    "fresh_for_seconds",
                    "scope",
                    "status",
                    "regions"
                  ],
                  "properties": {
                    "schema": {
                      "type": "string",
                      "enum": [
                        "voidly-vpn-availability/v1"
                      ]
                    },
                    "observed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "fresh_for_seconds": {
                      "type": "integer",
                      "enum": [
                        15
                      ]
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "node-control-health"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "available",
                        "degraded",
                        "unavailable",
                        "unknown"
                      ],
                      "description": "Fleet status, or the exact region result. An all-held fleet returns unavailable with an empty regions list. Unknown is an unestablished control result, not proof that a tunnel is down."
                    },
                    "regions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "city",
                          "country",
                          "status"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "available",
                              "unavailable",
                              "unknown"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed or repeated region query; not an availability verdict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpnAvailabilityError"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "HTTP response is not cacheable.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Unknown exact region ID; not an availability verdict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpnAvailabilityError"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "HTTP response is not cacheable.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Source-network read budget reached. Do not use the failed request as an availability verdict. Wait for Retry-After seconds or error.details.resetAt Unix seconds before another read.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before another request.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "Cache-Control": {
                "description": "HTTP response is not cacheable.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpnAvailabilityRateLimitError"
                }
              }
            }
          },
          "503": {
            "description": "Control policy, configuration, rate-limit meter, or configured fanout unavailable; not an availability verdict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpnAvailabilityError"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "HTTP response is not cacheable.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "VpnAvailabilityError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "description": "Optional error-specific details."
              }
            }
          }
        }
      },
      "VpnAvailabilityRateLimitError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "details"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "RATE_LIMIT_EXCEEDED"
                ]
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "required": [
                  "resetAt"
                ],
                "properties": {
                  "resetAt": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Unix seconds."
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
