{
  "components": {
    "headers": {
      "IdempotentReplayed": {
        "description": "`true` quando a resposta foi repetida de uma requisição anterior com a mesma `Idempotency-Key`. Convenção desta API, como a de outras; o rascunho da IETF não define cabeçalho de repetição.",
        "required": false,
        "schema": {
          "enum": [
            "true"
          ],
          "type": "string"
        }
      },
      "PollAfter": {
        "description": "Segundos sugeridos até consultar de novo (`GET /cloud/operations/{id}` ou o recurso): a operação segue em segundo plano e avança a cada 10 s.",
        "required": false,
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit": {
        "description": "O que resta do limite: `\"\u003cnome\u003e\";r=\u003crequisições restantes\u003e;t=\u003csegundos até o balde encher de novo\u003e`. A espera até a próxima requisição vem no `Retry-After`.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "RateLimitPolicy": {
        "description": "O limite que recusou: `\"\u003cnome\u003e\";q=\u003crajada\u003e;w=\u003csegundos para o balde encher de novo\u003e`.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Segundos até poder tentar novamente.",
        "required": false,
        "schema": {
          "type": "integer"
        }
      }
    },
    "parameters": {
      "Competence": {
        "description": "Mês de competência (`YYYY-MM`).",
        "in": "query",
        "name": "competence",
        "required": false,
        "schema": {
          "$ref": "#/components/schemas/Month"
        }
      },
      "ContractIdQuery": {
        "in": "query",
        "name": "contract_id",
        "required": false,
        "schema": {
          "format": "uuid",
          "type": "string"
        }
      },
      "DueFrom": {
        "description": "Vencimento a partir de (`YYYY-MM-DD`, inclusive).",
        "in": "query",
        "name": "due_from",
        "required": false,
        "schema": {
          "format": "date",
          "type": "string"
        }
      },
      "DueTo": {
        "description": "Vencimento até (`YYYY-MM-DD`, inclusive).",
        "in": "query",
        "name": "due_to",
        "required": false,
        "schema": {
          "format": "date",
          "type": "string"
        }
      },
      "EndCustomerRefQuery": {
        "description": "Id do cliente final no sistema do revendedor, o `end_customer_ref` do servidor.",
        "in": "query",
        "name": "end_customer_ref",
        "required": false,
        "schema": {
          "pattern": "^[A-Za-z0-9._:-]{1,128}$",
          "type": "string"
        }
      },
      "ExternalIdQuery": {
        "description": "Id do pedido no sistema do revendedor, o `external_id` do servidor.",
        "in": "query",
        "name": "external_id",
        "required": false,
        "schema": {
          "pattern": "^[A-Za-z0-9._:-]{1,128}$",
          "type": "string"
        }
      },
      "Id": {
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "format": "uuid",
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "description": "Chave de idempotência (draft-ietf-httpapi-idempotency-key-header-07): de 1 a 255 caracteres ASCII visíveis,\ncomo string de Structured Field (`\"…\"`) ou texto puro; gere uma nova (um UUID) para cada operação. Vale por 24 horas,\npor conta e credencial (o token, ou o usuário no painel). A mesma chave com a mesma requisição (método, caminho,\nquery e corpo) repete a resposta guardada (status e corpo) com `Idempotent-Replayed: true`, sem cobrar nem criar de\nnovo; com outra requisição responde 422 `idempotency.key_reused`; enquanto a primeira roda, 409\n`idempotency.in_progress` com `Retry-After`. Toda resposta fica guardada, inclusive 5xx (uma falha pode vir depois de\numa cobrança); só 401, 403, 409 e 429, que dizem que nada rodou, liberam a chave. Uma chave presa por uma requisição\nque morreu é liberada depois de 10 minutos.\n",
        "in": "header",
        "name": "Idempotency-Key",
        "required": false,
        "schema": {
          "maxLength": 257,
          "minLength": 1,
          "type": "string"
        }
      },
      "Limit": {
        "description": "Tamanho da página (padrão 50, máximo 200).",
        "in": "query",
        "name": "limit",
        "required": false,
        "schema": {
          "default": 50,
          "maximum": 200,
          "minimum": 1,
          "type": "integer"
        }
      },
      "Offset": {
        "description": "Deslocamento da página.",
        "in": "query",
        "name": "offset",
        "required": false,
        "schema": {
          "default": 0,
          "minimum": 0,
          "type": "integer"
        }
      },
      "PageCursor": {
        "description": "O `next_cursor` da página anterior desta mesma lista (opaco; 400 `invalid_cursor` se não for).",
        "in": "query",
        "name": "cursor",
        "required": false,
        "schema": {
          "maxLength": 200,
          "type": "string"
        }
      },
      "PageLimit": {
        "description": "Tamanho da página, de 1 a 100. Com `limit` ou `cursor` a resposta é uma página em ordem de criação (mais antigo\nprimeiro, depois o id) e `next_cursor` aponta a próxima; sem os dois vem a lista inteira, na ordem de sempre, com\n`next_cursor` nulo (compatibilidade). Com `cursor` e sem `limit`, páginas de 50.\n",
        "in": "query",
        "name": "limit",
        "required": false,
        "schema": {
          "maximum": 100,
          "minimum": 1,
          "type": "integer"
        }
      },
      "Search": {
        "description": "Texto de busca (case-insensitive, contém).",
        "in": "query",
        "name": "q",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "ServerId": {
        "in": "path",
        "name": "serverID",
        "required": true,
        "schema": {
          "format": "uuid",
          "type": "string"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Requisição inválida (JSON malformado, campo desconhecido, parâmetro inválido ou falha de validação)."
      },
      "Conflict": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Conflito (unicidade ou operação concorrente em andamento)."
      },
      "Forbidden": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Autenticado, mas sem permissão para a área ou a operação."
      },
      "NotFound": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Recurso não encontrado (ou fora do escopo do usuário)."
      },
      "ServiceUnavailable": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Dependência (gateway de pagamento, consulta de CEP) temporariamente indisponível."
      },
      "TooManyRequests": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Limite de requisições excedido. `Retry-After` diz em quantos segundos cabe a próxima requisição; os limites de\ntaxa também mandam `RateLimit-Policy` e `RateLimit` (draft-ietf-httpapi-ratelimit-headers).\n",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        }
      },
      "Unauthorized": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Não autenticado (token ausente, inválido ou expirado; credenciais inválidas)."
      },
      "UnprocessableEntity": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Entrada válida, mas viola uma regra de negócio."
      }
    },
    "schemas": {
      "CardType": {
        "enum": [
          "credit_card"
        ],
        "type": "string"
      },
      "ChargeStatus": {
        "enum": [
          "pending",
          "confirmed",
          "received",
          "overdue",
          "refunding",
          "refunded",
          "chargeback",
          "cancelled",
          "failed"
        ],
        "type": "string"
      },
      "CloudAssignInput": {
        "additionalProperties": false,
        "properties": {
          "server_id": {
            "description": "Servidor da mesma região, ou null para deixar o IP sem servidor.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "server_id"
        ],
        "type": "object"
      },
      "CloudCatalog": {
        "additionalProperties": false,
        "properties": {
          "available": {
            "description": "O motor de provisionamento está configurado.",
            "type": "boolean"
          },
          "internal_account": {
            "description": "Conta de uso interno - contrata sem cartão e sem cobrança.",
            "type": "boolean"
          },
          "os_images": {
            "items": {
              "$ref": "#/components/schemas/CloudOSImage"
            },
            "type": "array"
          },
          "plans": {
            "items": {
              "$ref": "#/components/schemas/CloudCatalogPlan"
            },
            "type": "array"
          },
          "simulated": {
            "description": "O motor é simulado (ambiente de validação) e nenhum servidor é instalado de verdade.",
            "type": "boolean"
          },
          "sites": {
            "items": {
              "$ref": "#/components/schemas/CloudSite"
            },
            "type": "array"
          },
          "verified": {
            "description": "A conta tem cartão salvo ou é de uso interno (não precisa).",
            "type": "boolean"
          }
        },
        "required": [
          "sites",
          "plans",
          "os_images",
          "verified",
          "available",
          "simulated",
          "internal_account"
        ],
        "type": "object"
      },
      "CloudCatalogPlan": {
        "additionalProperties": false,
        "properties": {
          "active": {
            "type": "boolean"
          },
          "bandwidth_mbps": {
            "description": "Limite de banda; 0 é a porta inteira.",
            "type": "integer"
          },
          "boot_slot": {
            "description": "Disco pelo qual o servidor inicia (id de disk_slots): o sistema precisa incluí-lo, sozinho ou num pool de discos iguais. As máquinas iniciam em BIOS legado só pelo primeiro disco. Ausente quando qualquer disco inicia o servidor.",
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "cores": {
            "type": "integer"
          },
          "cpu": {
            "type": "string"
          },
          "cpu_threads": {
            "type": "integer"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "disk_slots": {
            "description": "Os discos do plano um a um, onde o sistema pode ser instalado.",
            "items": {
              "$ref": "#/components/schemas/CloudDiskSlot"
            },
            "type": "array"
          },
          "disks": {
            "description": "Discos mínimos que a máquina precisa ter.",
            "items": {
              "$ref": "#/components/schemas/CloudDisk"
            },
            "type": "array"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "memory_gb": {
            "type": "integer"
          },
          "monthly_cents": {
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "port_speed_mbps": {
            "description": "Velocidade da porta entregue.",
            "type": "integer"
          },
          "product_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "raid1": {
            "description": "O plano tem dois discos iguais, então o servidor pode espelhá-los (RAID 1).",
            "type": "boolean"
          },
          "stock": {
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Máquinas em estoque por id de local.",
            "type": "object"
          },
          "traffic_tb": {
            "description": "Tráfego mensal em TB; 0 é ilimitado.",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "cpu",
          "cores",
          "cpu_threads",
          "memory_gb",
          "disks",
          "port_speed_mbps",
          "bandwidth_mbps",
          "traffic_tb",
          "monthly_cents",
          "product_id",
          "active",
          "created_at",
          "raid1",
          "disk_slots",
          "stock"
        ],
        "type": "object"
      },
      "CloudConsoleCursor": {
        "additionalProperties": false,
        "properties": {
          "col": {
            "type": "integer"
          },
          "hidden": {
            "type": "boolean"
          },
          "row": {
            "type": "integer"
          }
        },
        "required": [
          "row",
          "col",
          "hidden"
        ],
        "type": "object"
      },
      "CloudConsoleInput": {
        "additionalProperties": false,
        "properties": {
          "input": {
            "items": {
              "additionalProperties": false,
              "properties": {
                "key": {
                  "description": "Enter, Tab, Esc, Backspace, Delete, Insert, Home, End, PageUp, PageDown, Up, Down, Left, Right, Space, F1 a F12, Ctrl-A a Ctrl-Z.",
                  "type": "string"
                },
                "text": {
                  "type": "string"
                }
              },
              "type": "object"
            },
            "maxItems": 64,
            "minItems": 1,
            "type": "array"
          },
          "timeout_seconds": {
            "maximum": 25,
            "minimum": 0,
            "type": "integer"
          },
          "wait_for": {
            "maxLength": 200,
            "type": "string"
          }
        },
        "required": [
          "input"
        ],
        "type": "object"
      },
      "CloudConsoleOutput": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "description": "Bytes da saída em base64.",
            "format": "byte",
            "type": "string"
          },
          "idle_deadline": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "next_offset": {
            "format": "int64",
            "type": "integer"
          },
          "offset": {
            "format": "int64",
            "type": "integer"
          },
          "reset": {
            "type": "boolean"
          },
          "session": {
            "$ref": "#/components/schemas/CloudConsoleSession"
          }
        },
        "required": [
          "session",
          "offset",
          "next_offset",
          "reset",
          "data",
          "idle_deadline"
        ],
        "type": "object"
      },
      "CloudConsoleOverview": {
        "additionalProperties": false,
        "properties": {
          "available": {
            "type": "boolean"
          },
          "idle_timeout_seconds": {
            "type": "integer"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/CloudConsoleSession"
            },
            "type": "array"
          },
          "keys": {
            "description": "Teclas nomeadas aceitas pela entrada (sem Ctrl-Alt-Del e sem break).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "live": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CloudConsoleSession"
              }
            ],
            "nullable": true
          },
          "max_seconds": {
            "type": "integer"
          },
          "quota": {
            "$ref": "#/components/schemas/CloudQuota"
          },
          "unavailable": {
            "enum": [
              "",
              "server_busy",
              "suspended",
              "console_unavailable"
            ],
            "type": "string"
          }
        },
        "required": [
          "available",
          "unavailable",
          "idle_timeout_seconds",
          "max_seconds",
          "keys",
          "quota",
          "live",
          "items"
        ],
        "type": "object"
      },
      "CloudConsoleScreen": {
        "additionalProperties": false,
        "properties": {
          "cols": {
            "type": "integer"
          },
          "cursor": {
            "$ref": "#/components/schemas/CloudConsoleCursor"
          },
          "idle_deadline": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "lines": {
            "description": "As linhas da tela, sem espaços à direita. Saída do servidor (dado não confiável).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "matched": {
            "description": "Só na entrada com wait_for, se o texto apareceu na tela.",
            "type": "boolean"
          },
          "offset": {
            "format": "int64",
            "type": "integer"
          },
          "rows": {
            "type": "integer"
          },
          "session": {
            "$ref": "#/components/schemas/CloudConsoleSession"
          }
        },
        "required": [
          "session",
          "cols",
          "rows",
          "lines",
          "cursor",
          "offset",
          "idle_deadline"
        ],
        "type": "object"
      },
      "CloudConsoleSession": {
        "additionalProperties": false,
        "properties": {
          "activated_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "apply_error": {
            "type": "string"
          },
          "close_reason": {
            "description": "ended pelo cliente; staff pela equipe; idle 15 min sem uso; expired 2 horas; operation uma operação nossa começou no\nservidor; server_gone o servidor saiu da conta; failed não abriu; engine o broker perdeu a sessão (o SOL caiu).\n",
            "enum": [
              "",
              "ended",
              "staff",
              "idle",
              "expired",
              "operation",
              "server_gone",
              "failed",
              "engine"
            ],
            "type": "string"
          },
          "closed_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "hostname": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "idle_timeout_seconds": {
            "type": "integer"
          },
          "input_bytes": {
            "format": "int64",
            "type": "integer"
          },
          "input_count": {
            "type": "integer"
          },
          "opened_by": {
            "description": "Nome de quem abriu.",
            "type": "string"
          },
          "opened_via": {
            "enum": [
              "panel",
              "api",
              "mcp"
            ],
            "type": "string"
          },
          "server_id": {
            "format": "uuid",
            "type": "string"
          },
          "status": {
            "description": "closing = o acesso já terminou; a sessão do broker fecha e a gravação é guardada.",
            "enum": [
              "opening",
              "active",
              "closing",
              "closed"
            ],
            "type": "string"
          },
          "transcript": {
            "description": "A gravação (asciicast v2) da sessão; `saved` pode ser baixada.",
            "enum": [
              "pending",
              "saved",
              "unavailable"
            ],
            "type": "string"
          },
          "transcript_bytes": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "server_id",
          "hostname",
          "status",
          "close_reason",
          "apply_error",
          "opened_via",
          "opened_by",
          "expires_at",
          "idle_timeout_seconds",
          "input_count",
          "input_bytes",
          "created_at",
          "activated_at",
          "closed_at",
          "transcript",
          "transcript_bytes"
        ],
        "type": "object"
      },
      "CloudCouponCheckInput": {
        "additionalProperties": false,
        "properties": {
          "code": {
            "type": "string"
          },
          "plan_id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "code",
          "plan_id"
        ],
        "type": "object"
      },
      "CloudCouponQuote": {
        "additionalProperties": false,
        "properties": {
          "card_required": {
            "description": "O cartão continua obrigatório para o deploy.",
            "type": "boolean"
          },
          "code": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "first_discount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "first_gross_cents": {
            "format": "int64",
            "type": "integer"
          },
          "first_net_cents": {
            "format": "int64",
            "type": "integer"
          },
          "summary": {
            "description": "O desconto em palavras (ex. \"10% na primeira fatura\").",
            "type": "string"
          }
        },
        "required": [
          "code",
          "description",
          "summary",
          "first_gross_cents",
          "first_discount_cents",
          "first_net_cents",
          "card_required"
        ],
        "type": "object"
      },
      "CloudDeployInput": {
        "additionalProperties": false,
        "properties": {
          "coupon_code": {
            "description": "Cupom de desconto digitado no checkout; inválido recusa a compra com 422 (campo coupon_code).",
            "type": "string"
          },
          "description": {
            "maxLength": 500,
            "type": "string"
          },
          "disk_layout": {
            "description": "Legado (use storage, que tem precedência): single é o disco padrão (ext4 no Proxmox VE) e raid1 o espelho do primeiro par de discos iguais (ZFS RAID 1 no Proxmox VE), se o plano e o sistema permitem. Sem storage nem disk_layout: o Proxmox VE vai em ZFS RAID 1 no par de discos iguais com o disco de boot (ou ZFS num disco) e um sistema de imagem no menor disco que não seja HDD.",
            "enum": [
              "single",
              "raid1"
            ],
            "type": "string"
          },
          "end_customer_ref": {
            "description": "Id do cliente final no sistema do revendedor: agrupa servidores, não é único. Id opaco, sem dado pessoal\n(nem nome nem CPF), com a mesma regra do `external_id`.\nVazio ou ausente: sem referência.\n",
            "pattern": "^([A-Za-z0-9._:-]{1,128})?$",
            "type": "string"
          },
          "external_id": {
            "description": "Id do pedido no sistema do revendedor: único na conta entre os servidores não removidos. Id opaco, sem\ndado pessoal (nem nome nem CPF): de 1 a 128 caracteres entre letras sem acento, números e `. _ : -`.\nRepetido na conta: 409 `cloud.external_id_taken` com `existing_id`. Um servidor em remoção ainda ocupa o valor até\na remoção terminar (status `deleted`).\nVazio ou ausente: servidor sem referência.\n",
            "pattern": "^([A-Za-z0-9._:-]{1,128})?$",
            "type": "string"
          },
          "hostname": {
            "type": "string"
          },
          "os_image_id": {
            "format": "uuid",
            "type": "string"
          },
          "password": {
            "description": "Gera uma senha a cada instalação e a envia por e-mail a quem pediu.",
            "type": "boolean"
          },
          "plan_id": {
            "format": "uuid",
            "type": "string"
          },
          "site_id": {
            "format": "uuid",
            "type": "string"
          },
          "ssh_key_ids": {
            "items": {
              "format": "uuid",
              "type": "string"
            },
            "type": "array"
          },
          "storage": {
            "$ref": "#/components/schemas/CloudStorageInput"
          },
          "tags": {
            "items": {
              "pattern": "^[a-z0-9]([a-z0-9_.:=/-]{0,62}[a-z0-9])?$",
              "type": "string"
            },
            "maxItems": 20,
            "type": "array"
          },
          "user_data": {
            "description": "Documento do cloud-init: a primeira linha é `#cloud-config` (YAML com um mapa de chaves) ou um `#!` de\nscript, em UTF-8, até 64 KiB; pode vir em base64. Roda no primeiro boot de cada instalação, depois da configuração\nda plataforma: no `#cloud-config`, o que a plataforma define (login, senha, `disable_root`, `ssh_pwauth`) prevalece\ne as suas listas (`runcmd`, `packages`, `write_files`, `users`…) são acrescentadas às nossas; um script roda depois\ndo script de primeiro boot do sistema. Hostname, chaves e rede vêm dos metadados. Guardado cifrado; a API devolve só\n`user_data_sha256` e `user_data_size`. Só sistemas por imagem (422 `cloud.user_data_unsupported` no instalador do\nProxmox VE e em \"Sem sistema\"). Muda só com a reinstalação.\n",
            "maxLength": 90000,
            "type": "string"
          }
        },
        "required": [
          "site_id",
          "plan_id",
          "os_image_id",
          "hostname"
        ],
        "type": "object"
      },
      "CloudDisk": {
        "additionalProperties": false,
        "properties": {
          "count": {
            "type": "integer"
          },
          "model": {
            "type": "string"
          },
          "size_gb": {
            "type": "integer"
          },
          "type": {
            "enum": [
              "nvme",
              "ssd",
              "hdd"
            ],
            "type": "string"
          }
        },
        "required": [
          "count",
          "type",
          "size_gb"
        ],
        "type": "object"
      },
      "CloudDiskSlot": {
        "additionalProperties": false,
        "description": "Um disco do plano, um a um. O id fica igual enquanto os discos do plano não mudam.",
        "properties": {
          "id": {
            "description": "\u003ctipo\u003e-\u003cGB\u003e-\u003cn\u003e, por exemplo ssd-128-2: o segundo SSD de 128 GB do plano.",
            "example": "ssd-128-2",
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "size_gb": {
            "type": "integer"
          },
          "type": {
            "enum": [
              "nvme",
              "ssd",
              "hdd"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "size_gb",
          "model"
        ],
        "type": "object"
      },
      "CloudElasticIP": {
        "additionalProperties": false,
        "properties": {
          "address": {
            "description": "IPv4 público (/32).",
            "type": "string"
          },
          "apply_error": {
            "description": "Última resposta do gateway a uma mudança que não passou (\"\" quando não há).",
            "type": "string"
          },
          "contract_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "first_invoice_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "monthly_cents": {
            "format": "int64",
            "type": "integer"
          },
          "region": {
            "$ref": "#/components/schemas/CloudRegionRef"
          },
          "server_hostname": {
            "type": "string"
          },
          "server_id": {
            "description": "Servidor para onde o IP vai (null = sem servidor).",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "status": {
            "description": "pending_payment = primeira fatura em cobrança; configuring/moving/releasing = mudança indo para o gateway; failed = o gateway recusou ou não aplicou a tempo (veja apply_error).",
            "enum": [
              "pending_payment",
              "configuring",
              "active",
              "moving",
              "releasing",
              "failed"
            ],
            "type": "string"
          },
          "suspended_at": {
            "description": "Rota retirada por falta de pagamento; o endereço continua reservado e a rota volta quando a fatura é paga.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "id",
          "address",
          "region",
          "server_id",
          "server_hostname",
          "status",
          "apply_error",
          "monthly_cents",
          "contract_id",
          "first_invoice_id",
          "created_at",
          "suspended_at"
        ],
        "type": "object"
      },
      "CloudElasticIPInput": {
        "additionalProperties": false,
        "properties": {
          "region_id": {
            "format": "uuid",
            "type": "string"
          },
          "server_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "region_id"
        ],
        "type": "object"
      },
      "CloudElasticIPList": {
        "additionalProperties": false,
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/CloudElasticIP"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Cursor da próxima página (nulo na última e sem paginação; ausente nas listas da equipe).",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "CloudIPMIAccess": {
        "additionalProperties": false,
        "properties": {
          "address": {
            "description": "IP da IPMI, alcançável só pelo túnel.",
            "type": "string"
          },
          "password": {
            "description": "Só enquanto a sessão está viva e só para o cliente que opera os servidores (responsável); \"\" nos outros casos,\ninclusive para o usuário de consulta.\n",
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "username": {
            "description": "Conta da IPMI criada para a sessão (papel Operator).",
            "type": "string"
          }
        },
        "required": [
          "address",
          "url",
          "username",
          "password"
        ],
        "type": "object"
      },
      "CloudIPMIVPNInput": {
        "additionalProperties": false,
        "properties": {
          "duration_minutes": {
            "enum": [
              60,
              240,
              480
            ],
            "type": "integer"
          },
          "public_key": {
            "description": "Chave pública WireGuard do cliente (base64 de 32 bytes, como a saída de `wg pubkey`).",
            "type": "string"
          }
        },
        "required": [
          "duration_minutes",
          "public_key"
        ],
        "type": "object"
      },
      "CloudIPMIVPNOverview": {
        "additionalProperties": false,
        "properties": {
          "available": {
            "type": "boolean"
          },
          "durations": {
            "description": "Durações possíveis, em minutos.",
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/CloudIPMIVPNSession"
            },
            "type": "array"
          },
          "ports": {
            "items": {
              "$ref": "#/components/schemas/CloudIPMIVPNPort"
            },
            "type": "array"
          },
          "quota": {
            "$ref": "#/components/schemas/CloudQuota"
          },
          "unavailable": {
            "enum": [
              "",
              "server_busy",
              "region_without_vpn",
              "machine_without_bmc",
              "suspended"
            ],
            "type": "string"
          }
        },
        "required": [
          "available",
          "unavailable",
          "durations",
          "quota",
          "ports",
          "items"
        ],
        "type": "object"
      },
      "CloudIPMIVPNPort": {
        "additionalProperties": false,
        "properties": {
          "port": {
            "type": "integer"
          },
          "protocol": {
            "enum": [
              "tcp",
              "udp"
            ],
            "type": "string"
          },
          "use": {
            "type": "string"
          }
        },
        "required": [
          "protocol",
          "port",
          "use"
        ],
        "type": "object"
      },
      "CloudIPMIVPNSession": {
        "additionalProperties": false,
        "properties": {
          "activated_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "apply_error": {
            "description": "Última resposta da IPMI ou do OPNsense a uma mudança que não passou (\"\" quando não há).",
            "type": "string"
          },
          "bmc": {
            "$ref": "#/components/schemas/CloudIPMIAccess"
          },
          "close_reason": {
            "description": "Por que encerrou (ou está encerrando). operation = uma operação nossa (deploy, reinstalação, limpeza ou exclusão) começou no servidor; suspended = o servidor foi desligado por falta de pagamento.",
            "enum": [
              "",
              "ended",
              "staff",
              "expired",
              "server_gone",
              "failed",
              "operation",
              "suspended"
            ],
            "type": "string"
          },
          "closed_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "duration_minutes": {
            "type": "integer"
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "hostname": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "server_id": {
            "format": "uuid",
            "type": "string"
          },
          "status": {
            "description": "opening = conta da IPMI e peer sendo criados; closing = peer, regras e conta sendo removidos.",
            "enum": [
              "opening",
              "active",
              "closing",
              "closed"
            ],
            "type": "string"
          },
          "wireguard": {
            "$ref": "#/components/schemas/CloudWireGuardPeer"
          }
        },
        "required": [
          "id",
          "server_id",
          "hostname",
          "status",
          "close_reason",
          "apply_error",
          "duration_minutes",
          "expires_at",
          "created_at",
          "activated_at",
          "closed_at",
          "bmc",
          "wireguard"
        ],
        "type": "object"
      },
      "CloudNetworkMember": {
        "additionalProperties": false,
        "properties": {
          "address": {
            "description": "Endereço sugerido ao servidor na sub-rede.",
            "type": "string"
          },
          "apply_error": {
            "description": "Última resposta do switch a uma mudança que não passou (\"\" quando nenhuma).",
            "type": "string"
          },
          "hostname": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "server_id": {
            "format": "uuid",
            "type": "string"
          },
          "status": {
            "description": "A porta do switch do servidor, configurada pelo job `cloud-network`: connecting = a VLAN indo para a porta;\nfailed = o switch recusou ou não aplicou a tempo; removing = a VLAN saindo da porta.\n",
            "enum": [
              "connecting",
              "active",
              "failed",
              "removing"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "server_id",
          "hostname",
          "address",
          "status",
          "apply_error"
        ],
        "type": "object"
      },
      "CloudNetworkOverview": {
        "additionalProperties": false,
        "properties": {
          "quotas": {
            "additionalProperties": false,
            "properties": {
              "elastic_ips": {
                "$ref": "#/components/schemas/CloudQuota"
              },
              "firewalls": {
                "$ref": "#/components/schemas/CloudQuota"
              },
              "private_networks": {
                "$ref": "#/components/schemas/CloudQuota"
              }
            },
            "required": [
              "elastic_ips",
              "private_networks",
              "firewalls"
            ],
            "type": "object"
          },
          "regions": {
            "items": {
              "$ref": "#/components/schemas/CloudNetworkRegion"
            },
            "type": "array"
          },
          "verified": {
            "description": "A conta tem cartão salvo (IP elástico com preço exige).",
            "type": "boolean"
          }
        },
        "required": [
          "regions",
          "quotas",
          "verified"
        ],
        "type": "object"
      },
      "CloudNetworkRegion": {
        "additionalProperties": false,
        "properties": {
          "code": {
            "type": "string"
          },
          "elastic_ip_monthly_cents": {
            "description": "Preço mensal do próximo IP elástico da região.",
            "format": "int64",
            "type": "integer"
          },
          "elastic_ips": {
            "description": "IPs elásticos funcionam na região (o OPNsense é o gateway dos servidores) e há endereço livre.",
            "type": "boolean"
          },
          "firewalls": {
            "description": "O firewall funciona na região (o OPNsense é o gateway dos servidores).",
            "type": "boolean"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "private_networks": {
            "description": "Redes privadas funcionam na região (a plataforma configura o switch) e o bloco de rede privada tem espaço.",
            "type": "boolean"
          },
          "routed_by_gateway": {
            "description": "A rede da região faz IPs elásticos e firewall terem efeito (o OPNsense é o gateway dos servidores e está\ncadastrado), haja ou não endereço livre agora. Mover um IP elástico pede só isso.\n",
            "type": "boolean"
          },
          "servers": {
            "description": "Servidores da conta na região.",
            "type": "integer"
          },
          "switch_automated": {
            "description": "A plataforma configura as portas do switch da região, então redes privadas têm efeito, haja ou não espaço\nno bloco agora. Colocar um servidor numa rede pede só isso.\n",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "servers",
          "elastic_ips",
          "elastic_ip_monthly_cents",
          "firewalls",
          "private_networks",
          "routed_by_gateway",
          "switch_automated"
        ],
        "type": "object"
      },
      "CloudOSImage": {
        "additionalProperties": false,
        "properties": {
          "code": {
            "type": "string"
          },
          "family": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "install_method": {
            "description": "image grava uma imagem de disco; installer inicia o instalador oficial do sistema (Proxmox VE); none não instala nada (o cliente instala o próprio sistema pela IPMI).",
            "enum": [
              "image",
              "installer",
              "none"
            ],
            "type": "string"
          },
          "login_user": {
            "description": "Usuário de acesso ao sistema instalado.",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "password_required": {
            "description": "O sistema é usado com senha (painel web do Proxmox VE).",
            "type": "boolean"
          },
          "raid1": {
            "description": "O sistema instala num espelho (RAID 1) de dois discos iguais.",
            "type": "boolean"
          },
          "storage": {
            "$ref": "#/components/schemas/CloudStorageCaps"
          },
          "version": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "family",
          "version",
          "login_user",
          "password_required",
          "install_method",
          "raid1",
          "storage"
        ],
        "type": "object"
      },
      "CloudOperation": {
        "additionalProperties": false,
        "description": "Uma operação de um servidor, o contrato de espera da API: consulte até `status` ser `succeeded` ou `failed`\n(terminais). `pending` é a operação registrada que o motor ainda não começou; `running`, uma etapa em andamento.\n",
        "properties": {
          "error": {
            "description": "Resumo da etapa que falhou (vazio sem falha).",
            "type": "string"
          },
          "finished_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "requested_at": {
            "format": "date-time",
            "type": "string"
          },
          "server_id": {
            "format": "uuid",
            "type": "string"
          },
          "started_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "status": {
            "enum": [
              "pending",
              "running",
              "succeeded",
              "failed"
            ],
            "type": "string"
          },
          "steps": {
            "items": {
              "$ref": "#/components/schemas/CloudStep"
            },
            "type": "array"
          },
          "type": {
            "enum": [
              "deploy",
              "reinstall",
              "wipe",
              "delete"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "server_id",
          "type",
          "status",
          "steps",
          "error",
          "requested_at",
          "started_at",
          "finished_at"
        ],
        "type": "object"
      },
      "CloudOperationList": {
        "additionalProperties": false,
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/CloudOperation"
            },
            "type": "array"
          },
          "next_cursor": {
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "items",
          "next_cursor"
        ],
        "type": "object"
      },
      "CloudPlan": {
        "additionalProperties": false,
        "properties": {
          "active": {
            "type": "boolean"
          },
          "bandwidth_mbps": {
            "description": "Limite de banda; 0 é a porta inteira.",
            "type": "integer"
          },
          "boot_slot": {
            "description": "Disco pelo qual o servidor inicia (id de disk_slots): o sistema precisa incluí-lo, sozinho ou num pool de discos iguais. As máquinas iniciam em BIOS legado só pelo primeiro disco. Ausente quando qualquer disco inicia o servidor.",
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "cores": {
            "type": "integer"
          },
          "cpu": {
            "type": "string"
          },
          "cpu_threads": {
            "type": "integer"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "disk_slots": {
            "description": "Os discos do plano um a um, onde o sistema pode ser instalado.",
            "items": {
              "$ref": "#/components/schemas/CloudDiskSlot"
            },
            "type": "array"
          },
          "disks": {
            "description": "Discos mínimos que a máquina precisa ter.",
            "items": {
              "$ref": "#/components/schemas/CloudDisk"
            },
            "type": "array"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "memory_gb": {
            "type": "integer"
          },
          "monthly_cents": {
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "port_speed_mbps": {
            "description": "Velocidade da porta entregue.",
            "type": "integer"
          },
          "product_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "raid1": {
            "description": "O plano tem dois discos iguais, então o servidor pode espelhá-los (RAID 1).",
            "type": "boolean"
          },
          "traffic_tb": {
            "description": "Tráfego mensal em TB; 0 é ilimitado.",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "cpu",
          "cores",
          "cpu_threads",
          "memory_gb",
          "disks",
          "port_speed_mbps",
          "bandwidth_mbps",
          "traffic_tb",
          "monthly_cents",
          "product_id",
          "active",
          "created_at",
          "raid1",
          "disk_slots"
        ],
        "type": "object"
      },
      "CloudPrivateNetwork": {
        "additionalProperties": false,
        "properties": {
          "apply_error": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "region": {
            "$ref": "#/components/schemas/CloudRegionRef"
          },
          "servers": {
            "items": {
              "$ref": "#/components/schemas/CloudNetworkMember"
            },
            "type": "array"
          },
          "status": {
            "enum": [
              "configuring",
              "active",
              "deleting",
              "failed"
            ],
            "type": "string"
          },
          "subnet": {
            "description": "Sub-rede da rede (ex. 10.240.0.0/24).",
            "type": "string"
          },
          "vlan_id": {
            "description": "VID que o cliente configura no sistema (802.1Q).",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "region",
          "vlan_id",
          "subnet",
          "description",
          "status",
          "apply_error",
          "created_at",
          "servers"
        ],
        "type": "object"
      },
      "CloudPrivateNetworkInput": {
        "additionalProperties": false,
        "properties": {
          "description": {
            "description": "Até 80 caracteres.",
            "type": "string"
          },
          "region_id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "region_id",
          "description"
        ],
        "type": "object"
      },
      "CloudPrivateNetworkList": {
        "additionalProperties": false,
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/CloudPrivateNetwork"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Cursor da próxima página (nulo na última e sem paginação; ausente nas listas da equipe).",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "CloudPrivateNetworkUpdate": {
        "additionalProperties": false,
        "properties": {
          "description": {
            "type": "string"
          }
        },
        "required": [
          "description"
        ],
        "type": "object"
      },
      "CloudQuota": {
        "additionalProperties": false,
        "properties": {
          "limit": {
            "type": "integer"
          },
          "used": {
            "type": "integer"
          }
        },
        "required": [
          "used",
          "limit"
        ],
        "type": "object"
      },
      "CloudRAIDLevel": {
        "additionalProperties": false,
        "description": "Um nível de RAID oferecido e quantos discos ele usa. Mínimos do ZFS conforme pve.proxmox.com/wiki/ZFS_on_Linux: RAID 1 a partir de 2, RAID 10 a partir de 4 e em pares, RAIDZ-1 de 3, RAIDZ-2 de 4, RAIDZ-3 de 5; RAID 0 a partir de 2.",
        "properties": {
          "even_disks": {
            "description": "O nível usa discos em pares (RAID 10).",
            "type": "boolean"
          },
          "filesystems": {
            "description": "Sistemas de arquivos que oferecem o nível; vazio nos sistemas de imagem.",
            "items": {
              "enum": [
                "zfs",
                "ext4",
                "xfs"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "level": {
            "enum": [
              "single",
              "raid0",
              "raid1",
              "raid10",
              "raidz-1",
              "raidz-2",
              "raidz-3"
            ],
            "type": "string"
          },
          "max_disks": {
            "description": "0 é sem limite.",
            "type": "integer"
          },
          "min_disks": {
            "type": "integer"
          }
        },
        "required": [
          "level",
          "filesystems",
          "min_disks",
          "max_disks",
          "even_disks"
        ],
        "type": "object"
      },
      "CloudRegionRef": {
        "additionalProperties": false,
        "properties": {
          "code": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "code",
          "name"
        ],
        "type": "object"
      },
      "CloudSSHKey": {
        "additionalProperties": false,
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "fingerprint": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "public_key": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "fingerprint",
          "public_key",
          "created_at"
        ],
        "type": "object"
      },
      "CloudSSHKeyInput": {
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string"
          },
          "public_key": {
            "type": "string"
          }
        },
        "required": [
          "public_key"
        ],
        "type": "object"
      },
      "CloudSSHKeyList": {
        "additionalProperties": false,
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/CloudSSHKey"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Cursor da próxima página (nulo na última e sem paginação).",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "CloudSSHKeyUpdate": {
        "additionalProperties": false,
        "properties": {
          "name": {
            "description": "Novo nome (até 80 caracteres). A chave em si não muda; para outra chave, cadastre uma nova.",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "CloudServer": {
        "additionalProperties": false,
        "properties": {
          "cancel_at": {
            "description": "Último dia da assinatura cancelada (fim do período pago); depois dele a máquina volta ao estoque.",
            "format": "date",
            "nullable": true,
            "type": "string"
          },
          "contract_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "Descrição livre do cliente (até 500 caracteres).",
            "type": "string"
          },
          "disk_layout": {
            "description": "Legado (veja storage) - raid1 quando a última instalação é um RAID 1, senão single.",
            "enum": [
              "single",
              "raid1"
            ],
            "type": "string"
          },
          "end_customer_ref": {
            "description": "Id do cliente final no sistema do revendedor: agrupa servidores, não é único. Id opaco, sem dado pessoal\n(nem nome nem CPF), com a mesma regra do `external_id`.\nNulo quando não informado.\n",
            "nullable": true,
            "type": "string"
          },
          "external_id": {
            "description": "Id do pedido no sistema do revendedor: único na conta entre os servidores não removidos. Id opaco, sem\ndado pessoal (nem nome nem CPF): de 1 a 128 caracteres entre letras sem acento, números e `. _ : -`.\nNulo quando não informado.\n",
            "nullable": true,
            "type": "string"
          },
          "first_invoice_id": {
            "description": "Fatura cobrada na compra.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "hostname": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "ipv4": {
            "type": "string"
          },
          "ipv4_gateway": {
            "type": "string"
          },
          "ipv4_prefix": {
            "type": "integer"
          },
          "monthly_cents": {
            "description": "O que o servidor custa por mês: a recorrência do contrato dele (com reajustes), não o preço atual do plano, que vale só para servidores novos.",
            "format": "int64",
            "type": "integer"
          },
          "operation": {
            "enum": [
              "deploy",
              "reinstall",
              "wipe",
              "delete",
              ""
            ],
            "type": "string"
          },
          "operation_id": {
            "description": "A última operação do servidor (deploy, reinstall, wipe ou delete); acompanhe por `GET /cloud/operations/{id}`.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "os": {
            "$ref": "#/components/schemas/CloudOSImage"
          },
          "password_login": {
            "description": "Cada instalação gera uma senha e a envia por e-mail.",
            "type": "boolean"
          },
          "plan": {
            "$ref": "#/components/schemas/CloudPlan"
          },
          "power": {
            "enum": [
              "on",
              "off",
              "unknown"
            ],
            "type": "string"
          },
          "site": {
            "$ref": "#/components/schemas/CloudSite"
          },
          "ssh_keys": {
            "description": "Quantidade de chaves instaladas no último deploy.",
            "type": "integer"
          },
          "status": {
            "enum": [
              "pending_payment",
              "deploying",
              "active",
              "failed",
              "wiping",
              "empty",
              "deleting",
              "deleted"
            ],
            "type": "string"
          },
          "steps": {
            "items": {
              "$ref": "#/components/schemas/CloudStep"
            },
            "type": "array"
          },
          "storage": {
            "$ref": "#/components/schemas/CloudStorage"
          },
          "suspended_at": {
            "description": "Desligado por falta de pagamento, com os dados mantidos; volta a ser ligado sozinho quando a fatura é\npaga. Enquanto isso, power, reinstalação, apagar, cancelar e VPN da IPMI respondem 409 `cloud.server_suspended`.\n",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "tags": {
            "description": "Tags do cliente, em ordem alfabética e sem repetição.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "terminated_at": {
            "description": "Encerrado na hora pela equipe. O cliente continua vendo a página do servidor (o aviso leva\naté ela) mesmo depois de removido, só com o texto fixo da categoria.\n",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "termination_category": {
            "description": "Por que a equipe encerrou o servidor (vazio quando não foi encerrado pela equipe).",
            "enum": [
              "",
              "abuse",
              "fraud",
              "customer_request",
              "internal_test",
              "delinquency",
              "other"
            ],
            "type": "string"
          },
          "user_data_sha256": {
            "description": "SHA-256 (hex) do user data das instalações; o conteúdo nunca é devolvido. Nulo sem user data.",
            "nullable": true,
            "type": "string"
          },
          "user_data_size": {
            "description": "Tamanho do user data em bytes (0 sem user data).",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "hostname",
          "status",
          "power",
          "site",
          "plan",
          "os",
          "storage",
          "disk_layout",
          "ipv4",
          "ipv4_prefix",
          "ipv4_gateway",
          "ssh_keys",
          "password_login",
          "contract_id",
          "monthly_cents",
          "first_invoice_id",
          "cancel_at",
          "suspended_at",
          "operation",
          "steps",
          "created_at",
          "terminated_at",
          "termination_category"
        ],
        "type": "object"
      },
      "CloudServerList": {
        "additionalProperties": false,
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/CloudServer"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Cursor da próxima página (nulo na última e sem paginação).",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "CloudServerRef": {
        "additionalProperties": false,
        "properties": {
          "server_id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "server_id"
        ],
        "type": "object"
      },
      "CloudServerUpdate": {
        "additionalProperties": false,
        "description": "Campos ausentes ficam como estão (JSON merge patch, RFC 7396); `tags` vazio, `description` vazia e\n`external_id` ou `end_customer_ref` vazios limpam.\n",
        "properties": {
          "description": {
            "maxLength": 500,
            "type": "string"
          },
          "end_customer_ref": {
            "description": "Id do cliente final no sistema do revendedor: agrupa servidores, não é único. Id opaco, sem dado pessoal\n(nem nome nem CPF), com a mesma regra do `external_id`.\nVazio limpa.\n",
            "pattern": "^([A-Za-z0-9._:-]{1,128})?$",
            "type": "string"
          },
          "external_id": {
            "description": "Id do pedido no sistema do revendedor: único na conta entre os servidores não removidos. Id opaco, sem\ndado pessoal (nem nome nem CPF): de 1 a 128 caracteres entre letras sem acento, números e `. _ : -`.\nVazio limpa; o de outro servidor da conta responde 409 `cloud.external_id_taken` com `existing_id`.\n",
            "pattern": "^([A-Za-z0-9._:-]{1,128})?$",
            "type": "string"
          },
          "hostname": {
            "description": "Mesma regra do deploy (letras minúsculas, números e hífen, até 63). Muda só o nome na plataforma (listas, e-mails, a\nconfirmação das ações destrutivas); o sistema mantém o hostname da instalação até a próxima reinstalação, que usa o novo.\nO título do contrato não muda.\n",
            "type": "string"
          },
          "tags": {
            "items": {
              "pattern": "^[a-z0-9]([a-z0-9_.:=/-]{0,62}[a-z0-9])?$",
              "type": "string"
            },
            "maxItems": 20,
            "type": "array"
          }
        },
        "type": "object"
      },
      "CloudSite": {
        "additionalProperties": false,
        "properties": {
          "active": {
            "type": "boolean"
          },
          "city": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "region_id": {
            "description": "Região do local; os IPs das máquinas vêm dos blocos dela.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "id",
          "region_id",
          "code",
          "name",
          "city",
          "country",
          "active",
          "created_at"
        ],
        "type": "object"
      },
      "CloudStep": {
        "additionalProperties": false,
        "properties": {
          "finished_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "name": {
            "enum": [
              "prepare",
              "erase",
              "install",
              "configure",
              "reboot",
              "ready",
              "release"
            ],
            "type": "string"
          },
          "started_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "status": {
            "enum": [
              "pending",
              "running",
              "done",
              "failed"
            ],
            "type": "string"
          }
        },
        "required": [
          "name",
          "status",
          "message",
          "started_at",
          "finished_at"
        ],
        "type": "object"
      },
      "CloudStorage": {
        "additionalProperties": false,
        "description": "Onde a última instalação pôs o sistema.",
        "properties": {
          "disks": {
            "items": {
              "$ref": "#/components/schemas/CloudDiskSlot"
            },
            "type": "array"
          },
          "filesystem": {
            "description": "Vazio nos sistemas de imagem (o sistema de arquivos é o da imagem).",
            "enum": [
              "zfs",
              "ext4",
              "xfs",
              ""
            ],
            "type": "string"
          },
          "raid": {
            "enum": [
              "single",
              "raid0",
              "raid1",
              "raid10",
              "raidz-1",
              "raidz-2",
              "raidz-3"
            ],
            "type": "string"
          },
          "summary": {
            "example": "ZFS RAID 1 em 2× SSD 128 GB",
            "type": "string"
          }
        },
        "required": [
          "disks",
          "filesystem",
          "raid",
          "summary"
        ],
        "type": "object"
      },
      "CloudStorageCaps": {
        "additionalProperties": false,
        "description": "O que o cliente escolhe nos discos deste sistema. O instalador do Proxmox VE grava ZFS (padrão, recomendado), ext4 ou XFS; ext4 e XFS vão num disco, o ZFS num disco ou nos níveis de RAID. Um sistema de imagem vai num disco (o sistema de arquivos é o da imagem) ou, quando o motor tem a versão dele para espelho, em RAID 1 de dois discos iguais.",
        "properties": {
          "default_filesystem": {
            "enum": [
              "zfs",
              "ext4",
              "xfs",
              ""
            ],
            "type": "string"
          },
          "filesystems": {
            "description": "Sistemas de arquivos que o instalador grava; vazio nos sistemas de imagem.",
            "items": {
              "enum": [
                "zfs",
                "ext4",
                "xfs"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "levels": {
            "items": {
              "$ref": "#/components/schemas/CloudRAIDLevel"
            },
            "type": "array"
          }
        },
        "required": [
          "filesystems",
          "default_filesystem",
          "levels"
        ],
        "type": "object"
      },
      "CloudStorageInput": {
        "additionalProperties": false,
        "description": "Os discos do plano (ids de disk_slots), o sistema de arquivos e o nível de RAID. Um pool usa discos iguais (mesmo tipo e tamanho); ext4 e XFS usam um disco; um sistema de imagem usa um disco, ou dois em RAID 1 quando oferece; cada nível do ZFS pede o seu mínimo de discos; a escolha inclui o boot_slot do plano, quando ele existe. Os discos fora da escolha ficam livres.",
        "properties": {
          "disks": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "filesystem": {
            "description": "zfs (padrão no Proxmox VE), ext4 ou xfs; vazio nos sistemas de imagem.",
            "type": "string"
          },
          "raid": {
            "description": "single, raid0, raid1, raid10, raidz-1, raidz-2 ou raidz-3; obrigatório no ZFS com mais de um disco.",
            "type": "string"
          }
        },
        "required": [
          "disks"
        ],
        "type": "object"
      },
      "CloudWireGuardPeer": {
        "additionalProperties": false,
        "properties": {
          "address": {
            "description": "Endereço do cliente no túnel (/32).",
            "type": "string"
          },
          "allowed_ips": {
            "description": "Só a IPMI do servidor (/32).",
            "type": "string"
          },
          "config": {
            "description": "Arquivo do wg-quick com `\u003cSUA_CHAVE_PRIVADA\u003e` no lugar da chave privada (\"\" até ativa).",
            "type": "string"
          },
          "endpoint": {
            "description": "host:porta do WireGuard do OPNsense.",
            "type": "string"
          },
          "keepalive": {
            "type": "integer"
          },
          "public_key": {
            "description": "Chave pública do cliente.",
            "type": "string"
          },
          "server_public_key": {
            "description": "Chave pública do OPNsense (\"\" até a sessão ficar ativa).",
            "type": "string"
          }
        },
        "required": [
          "address",
          "public_key",
          "server_public_key",
          "endpoint",
          "allowed_ips",
          "keepalive",
          "config"
        ],
        "type": "object"
      },
      "DiscountSource": {
        "description": "contract - desconto da equipe; coupon - cupom; internal - uso interno (conta interna); minimum - saldo abaixo do\nmínimo de cobrança (R$ 5,00) dispensado.\n",
        "enum": [
          "contract",
          "coupon",
          "internal",
          "minimum"
        ],
        "type": "string"
      },
      "Gateway": {
        "description": "Como a fatura é recebida. `manual` = Pix direto na conta da empresa, baixado por conciliação ou manualmente.\n`stripe` e `asaas` significam \"cobrada num gateway\" e aceitam Pix, boleto e cartão: cada cobrança nova sai pelo\nprovedor configurado para a forma (`PIX_PROVIDER`, `BOLETO_PROVIDER`, `CARD_PROVIDER`; veja `GET\n/public/card-config`), a cobrança já emitida termina no provedor que a emitiu e um cartão salvo é sempre cobrado\nno provedor que o salvou. Contratos novos nascem com o provedor configurado (Stripe quando ela está\nconfigurada).\n",
        "enum": [
          "asaas",
          "stripe",
          "nupay",
          "manual"
        ],
        "type": "string"
      },
      "InvoiceStatus": {
        "description": "`courtesy`: fatura de conta interna (cortesia), com 100% de desconto \"Uso interno\"; `no_charge`: descontos\ncobriram o valor todo. Nas duas não há nada a pagar: nunca vão ao gateway, à NFS-e nem à inadimplência.\n",
        "enum": [
          "open",
          "paid",
          "overdue",
          "cancelled",
          "refunded",
          "courtesy",
          "no_charge"
        ],
        "type": "string"
      },
      "LateTerms": {
        "additionalProperties": false,
        "description": "Multa e juros por atraso, combinados no contrato, em pontos-base (200 = 2,00%). A multa incide uma vez;\nos juros são ao mês, pro rata die. Zero = sem multa/juros. Pix e boleto do Asaas levam os termos; cobranças\nemitidas após o vencimento e o Pix direto já incluem o que acumulou.\n",
        "properties": {
          "fee_bps": {
            "description": "Multa (% do valor), em pontos-base.",
            "maximum": 1000,
            "minimum": 0,
            "type": "integer"
          },
          "interest_bps": {
            "description": "Juros ao mês, em pontos-base.",
            "maximum": 1000,
            "minimum": 0,
            "type": "integer"
          },
          "policy": {
            "description": "Como os termos se aplicam, definido pelo sistema (ignorado nas requisições). `standard`: multa e juros desde o\ndia seguinte ao vencimento, o Asaas soma em Pix e boleto. `cloud` (contratos de servidores e IPs\nelásticos): sem multa nem juros até o 15º dia após o vencimento (dia útil); depois, juros desde o dia seguinte\nao vencimento e multa só em Pix e boleto (nunca no cartão), calculados pela plataforma; o Asaas não soma nada.\n",
            "enum": [
              "standard",
              "cloud"
            ],
            "readOnly": true,
            "type": "string"
          }
        },
        "required": [
          "fee_bps",
          "interest_bps"
        ],
        "type": "object"
      },
      "Me": {
        "additionalProperties": false,
        "properties": {
          "permissions": {
            "items": {
              "$ref": "#/components/schemas/Permission"
            },
            "type": "array"
          },
          "user": {
            "$ref": "#/components/schemas/User"
          }
        },
        "required": [
          "user",
          "permissions"
        ],
        "type": "object"
      },
      "Month": {
        "description": "Mês no formato `YYYY-MM`.",
        "example": "2026-10",
        "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
        "type": "string"
      },
      "PageMeta": {
        "additionalProperties": false,
        "properties": {
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "limit",
          "offset",
          "total"
        ],
        "type": "object"
      },
      "PaymentMethod": {
        "additionalProperties": false,
        "description": "Cartão tokenizado.",
        "properties": {
          "brand": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "customer_id": {
            "format": "uuid",
            "type": "string"
          },
          "expiry_month": {
            "type": "integer"
          },
          "expiry_year": {
            "type": "integer"
          },
          "holder_name": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "is_default": {
            "type": "boolean"
          },
          "last4": {
            "type": "string"
          },
          "provider": {
            "$ref": "#/components/schemas/Gateway"
          },
          "type": {
            "$ref": "#/components/schemas/CardType"
          }
        },
        "required": [
          "id",
          "customer_id",
          "provider",
          "type",
          "brand",
          "last4",
          "holder_name",
          "expiry_month",
          "expiry_year",
          "is_default",
          "created_at"
        ],
        "type": "object"
      },
      "PaymentMethodList": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/PaymentMethod"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "type": "object"
      },
      "PaymentMethodType": {
        "description": "Forma de pagamento.",
        "enum": [
          "pix",
          "boleto",
          "credit_card"
        ],
        "type": "string"
      },
      "Permission": {
        "enum": [
          "customers:read",
          "customers:write",
          "contracts:read",
          "contracts:write",
          "billing:read",
          "billing:write",
          "finance:read",
          "finance:write",
          "reports:read",
          "users:manage",
          "jobs:run",
          "portal:read",
          "portal:payments",
          "cloud:read",
          "cloud:write",
          "cloud:operate",
          "support:read",
          "support:write",
          "support:manage",
          "portal:support",
          "discounts:write",
          "discounts:approve",
          "customers:internal",
          "cloud:terminate",
          "apidocs:read"
        ],
        "type": "string"
      },
      "PortalCardAction": {
        "additionalProperties": false,
        "description": "Pagamento com cartão esperando o cliente confirmar (o banco emissor pediu autenticação 3D Secure). A página\nbusca `GET .../card-action` e confirma com o Stripe.js.\n",
        "properties": {
          "amount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "charge_id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "charge_id",
          "amount_cents"
        ],
        "type": "object"
      },
      "PortalCharge": {
        "additionalProperties": false,
        "description": "Cobrança como vista pelo cliente no portal.",
        "properties": {
          "action_required": {
            "description": "Pagamento com cartão esperando o cliente confirmar (3D Secure pedido pelo banco).",
            "type": "boolean"
          },
          "amount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "automatic": {
            "description": "Debitada por Pix Automático no vencimento (não há o que pagar manualmente).",
            "type": "boolean"
          },
          "bank_slip_url": {
            "description": "PDF do boleto. No link público, só enquanto a fatura e o boleto estão em aberto.",
            "type": "string"
          },
          "boleto_line": {
            "description": "Linha digitável do boleto (vazia quando o provedor não a informa). No link público, só com o boleto em aberto.",
            "type": "string"
          },
          "confirmation_business_days": {
            "description": "Boleto pago é confirmado em até N dias úteis (1 na Stripe, 3 no Asaas); 0 nas demais formas.",
            "type": "integer"
          },
          "due_date": {
            "format": "date",
            "type": "string"
          },
          "expires_at": {
            "description": "Até quando o Pix ou o boleto pode ser pago; nulo quando continua pagável depois do vencimento.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "method": {
            "$ref": "#/components/schemas/PaymentMethodType"
          },
          "paid_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/ChargeStatus"
          }
        },
        "required": [
          "id",
          "method",
          "status",
          "amount_cents",
          "due_date",
          "paid_at",
          "bank_slip_url"
        ],
        "type": "object"
      },
      "PortalInvoice": {
        "additionalProperties": false,
        "description": "Fatura como vista pelo cliente no portal (sem taxas, ids internos ou erros do gateway).",
        "properties": {
          "amount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "amount_due_cents": {
            "description": "Valor para pagar hoje (com multa e juros quando em atraso); 0 se a fatura não está em aberto.",
            "format": "int64",
            "type": "integer"
          },
          "cancelled_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "card_action": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PortalCardAction"
              }
            ],
            "description": "Pagamento com cartão esperando o cliente confirmar (3D Secure); nulo quando não há.",
            "nullable": true
          },
          "charges": {
            "description": "Presente apenas no detalhe e quando há cobranças acionáveis (exclui canceladas e recusadas).",
            "items": {
              "$ref": "#/components/schemas/PortalCharge"
            },
            "type": "array"
          },
          "competence": {
            "description": "Primeiro dia do mês de competência (`YYYY-MM-01`).",
            "format": "date",
            "type": "string"
          },
          "contract_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "discount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "due_date": {
            "format": "date",
            "type": "string"
          },
          "gateway": {
            "$ref": "#/components/schemas/Gateway"
          },
          "gross_amount_cents": {
            "description": "Valor cheio (bruto); amount_cents é o que se paga.",
            "format": "int64",
            "type": "integer"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "late_terms": {
            "$ref": "#/components/schemas/LateTerms"
          },
          "lines": {
            "description": "Linhas de cobrança e de desconto (só no detalhe).",
            "items": {
              "$ref": "#/components/schemas/PortalInvoiceLine"
            },
            "type": "array"
          },
          "number": {
            "type": "string"
          },
          "paid_amount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "paid_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "payment_issue": {
            "description": "Aviso ao cliente quando a última tentativa no cartão falhou; vazio caso contrário.",
            "type": "string"
          },
          "payment_method": {
            "$ref": "#/components/schemas/PaymentMethodType"
          },
          "payment_method_id": {
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "payment_url": {
            "description": "Link público de pagamento, para compartilhar com quem vai pagar (só no detalhe).",
            "type": "string"
          },
          "period_end": {
            "format": "date",
            "nullable": true,
            "type": "string"
          },
          "period_start": {
            "format": "date",
            "nullable": true,
            "type": "string"
          },
          "refunded_amount_cents": {
            "description": "Devolvido ao cliente até agora (estorno parcial ou total).",
            "format": "int64",
            "type": "integer"
          },
          "refunded_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "service_invoice": {
            "$ref": "#/components/schemas/PortalServiceInvoice"
          },
          "status": {
            "$ref": "#/components/schemas/InvoiceStatus"
          },
          "subscription_id": {
            "description": "Recorrência que gerou a fatura (nula em faturas avulsas).",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "gross_amount_cents",
          "discount_cents",
          "late_terms",
          "amount_due_cents",
          "id",
          "number",
          "contract_id",
          "description",
          "amount_cents",
          "paid_amount_cents",
          "refunded_amount_cents",
          "status",
          "due_date",
          "competence",
          "period_start",
          "period_end",
          "payment_method",
          "gateway",
          "payment_method_id",
          "paid_at",
          "cancelled_at",
          "refunded_at",
          "payment_issue"
        ],
        "type": "object"
      },
      "PortalInvoiceLine": {
        "additionalProperties": false,
        "properties": {
          "amount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "description": {
            "type": "string"
          },
          "discount_source": {
            "$ref": "#/components/schemas/DiscountSource"
          },
          "kind": {
            "enum": [
              "charge",
              "discount"
            ],
            "type": "string"
          },
          "quantity": {
            "type": "integer"
          }
        },
        "required": [
          "kind",
          "description",
          "quantity",
          "amount_cents"
        ],
        "type": "object"
      },
      "PortalInvoicePage": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/PortalInvoice"
            },
            "type": "array"
          },
          "meta": {
            "$ref": "#/components/schemas/PageMeta"
          }
        },
        "required": [
          "data",
          "meta"
        ],
        "type": "object"
      },
      "PortalServiceInvoice": {
        "additionalProperties": false,
        "description": "Nota fiscal da fatura como o cliente a vê.",
        "properties": {
          "cancelled": {
            "type": "boolean"
          },
          "issued_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "number": {
            "type": "string"
          },
          "pdf_url": {
            "type": "string"
          },
          "xml_url": {
            "type": "string"
          }
        },
        "required": [
          "number",
          "issued_at",
          "pdf_url",
          "xml_url",
          "cancelled"
        ],
        "type": "object"
      },
      "PortalTicket": {
        "additionalProperties": false,
        "properties": {
          "can_rate": {
            "description": "Resolvido por alguém há até 28 dias; pode ser avaliado.",
            "type": "boolean"
          },
          "category": {
            "$ref": "#/components/schemas/TicketCategory"
          },
          "closed": {
            "description": "Resolvido há mais dias que `close_days`; uma mensagem nova abre um chamado de continuação.",
            "type": "boolean"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "expected": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TicketExpectation"
              }
            ],
            "description": "Quando a equipe deve responder (só enquanto espera a equipe).",
            "nullable": true
          },
          "follow_up_of": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TicketRef"
              }
            ],
            "nullable": true
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "last_from": {
            "enum": [
              "customer",
              "staff",
              ""
            ],
            "type": "string"
          },
          "last_message_at": {
            "format": "date-time",
            "type": "string"
          },
          "merged_into": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TicketRef"
              }
            ],
            "nullable": true
          },
          "messages": {
            "description": "A conversa (só no detalhe).",
            "items": {
              "$ref": "#/components/schemas/PortalTicketMessage"
            },
            "type": "array"
          },
          "number": {
            "example": "SUP-000123",
            "type": "string"
          },
          "priority": {
            "$ref": "#/components/schemas/TicketPriority"
          },
          "purged": {
            "description": "A conversa foi apagada pelo prazo de guarda (1 ano após a resolução).",
            "type": "boolean"
          },
          "rating": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TicketRating"
              }
            ],
            "nullable": true
          },
          "resolved_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "server": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TicketServer"
              }
            ],
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/TicketStatus"
          },
          "subject": {
            "type": "string"
          },
          "unread": {
            "description": "Há resposta da equipe que a conta ainda não leu.",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "number",
          "subject",
          "category",
          "status",
          "priority",
          "server",
          "last_message_at",
          "last_from",
          "unread",
          "expected",
          "closed",
          "merged_into",
          "follow_up_of",
          "rating",
          "can_rate",
          "created_at",
          "resolved_at",
          "purged"
        ],
        "type": "object"
      },
      "PortalTicketList": {
        "additionalProperties": false,
        "properties": {
          "counts": {
            "additionalProperties": false,
            "description": "Chamados da conta em andamento (abertos ou respondidos) e resolvidos, em todas as páginas.",
            "properties": {
              "active": {
                "format": "int64",
                "type": "integer"
              },
              "resolved": {
                "format": "int64",
                "type": "integer"
              }
            },
            "required": [
              "active",
              "resolved"
            ],
            "type": "object"
          },
          "data": {
            "items": {
              "$ref": "#/components/schemas/PortalTicket"
            },
            "type": "array"
          },
          "meta": {
            "$ref": "#/components/schemas/PageMeta"
          }
        },
        "required": [
          "data",
          "meta",
          "counts"
        ],
        "type": "object"
      },
      "PortalTicketMessage": {
        "additionalProperties": false,
        "properties": {
          "attachments": {
            "items": {
              "$ref": "#/components/schemas/TicketAttachment"
            },
            "type": "array"
          },
          "author": {
            "enum": [
              "customer",
              "staff",
              "system"
            ],
            "type": "string"
          },
          "author_name": {
            "description": "Quem escreveu; toda mensagem da equipe aparece como \"Equipe Halver\" e as do sistema como \"Halver\".",
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "id",
          "author",
          "author_name",
          "body",
          "attachments",
          "created_at"
        ],
        "type": "object"
      },
      "Problem": {
        "additionalProperties": false,
        "description": "RFC 9457 problem details.",
        "properties": {
          "code": {
            "description": "Código estável, ex. `validation_failed`, `contract.invalid_transition`, `customer.not_found`.",
            "type": "string"
          },
          "detail": {
            "description": "Mensagem legível (pt-BR). Omitida quando vazia.",
            "type": "string"
          },
          "errors": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Mensagens por campo (em `validation_failed` e `request.invalid_json`).",
            "type": "object"
          },
          "existing_id": {
            "description": "Num conflito com um registro que já existe, o id dele (ex. o servidor com o mesmo `external_id` em `cloud.external_id_taken`).",
            "format": "uuid",
            "type": "string"
          },
          "request_id": {
            "description": "Mesmo valor do header `X-Request-ID`.",
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "title": {
            "description": "Texto padrão do status HTTP (ex. `Bad Request`).",
            "type": "string"
          },
          "type": {
            "description": "Sempre `about:blank`.",
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "type": "object"
      },
      "Receipt": {
        "additionalProperties": false,
        "description": "Comprovante de pagamento. O que ele atesta é copiado na emissão; `status` é lido da fatura a cada consulta.",
        "properties": {
          "adjustment_cents": {
            "description": "Outra diferença entre o pago e o valor da fatura (negativa = desconto), ex. baixa registrada pela equipe.",
            "format": "int64",
            "type": "integer"
          },
          "amount_cents": {
            "description": "Valor da fatura.",
            "format": "int64",
            "type": "integer"
          },
          "code": {
            "description": "Código de validação, `XXXX-XXXX-XXXX-XXXX`.",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "due_date": {
            "format": "date",
            "type": "string"
          },
          "invoice_number": {
            "type": "string"
          },
          "issued_at": {
            "format": "date-time",
            "type": "string"
          },
          "issuer_document": {
            "description": "CNPJ de quem recebeu, formatado.",
            "type": "string"
          },
          "issuer_name": {
            "description": "Quem recebeu (razão social).",
            "type": "string"
          },
          "late_cents": {
            "description": "Multa e juros pagos além do valor da fatura (pagamento após o vencimento).",
            "format": "int64",
            "type": "integer"
          },
          "method": {
            "description": "`bank_transfer`: Pix/transferência direto na conta da empresa; `manual`: registrado pela equipe.",
            "enum": [
              "pix",
              "boleto",
              "credit_card",
              "bank_transfer",
              "manual"
            ],
            "type": "string"
          },
          "paid_amount_cents": {
            "format": "int64",
            "type": "integer"
          },
          "paid_at": {
            "format": "date-time",
            "type": "string"
          },
          "payer_document": {
            "description": "CNPJ completo ou CPF mascarado.",
            "type": "string"
          },
          "payer_name": {
            "type": "string"
          },
          "refunded_amount_cents": {
            "description": "Devolvido ao pagador (estorno parcial mantém `valid`).",
            "format": "int64",
            "type": "integer"
          },
          "refunded_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "status": {
            "enum": [
              "valid",
              "refunded",
              "reverted"
            ],
            "type": "string"
          },
          "url": {
            "description": "Página pública do comprovante (para o QR code e para compartilhar).",
            "type": "string"
          }
        },
        "required": [
          "code",
          "url",
          "status",
          "refunded_at",
          "refunded_amount_cents",
          "issuer_name",
          "issuer_document",
          "payer_name",
          "payer_document",
          "invoice_number",
          "description",
          "due_date",
          "amount_cents",
          "late_cents",
          "adjustment_cents",
          "paid_amount_cents",
          "paid_at",
          "method",
          "issued_at"
        ],
        "type": "object"
      },
      "Role": {
        "description": "Papel do usuário. Staff - director, finance, sales, support. Portal - owner, viewer.",
        "enum": [
          "director",
          "finance",
          "sales",
          "support",
          "owner",
          "viewer"
        ],
        "type": "string"
      },
      "TicketAttachment": {
        "additionalProperties": false,
        "properties": {
          "content_type": {
            "enum": [
              "image/png",
              "image/jpeg",
              "image/gif",
              "image/webp",
              "application/pdf",
              "text/plain"
            ],
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "file_name": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "size_bytes": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "file_name",
          "content_type",
          "size_bytes",
          "created_at"
        ],
        "type": "object"
      },
      "TicketCategory": {
        "description": "Do que se trata: vendas, suporte técnico, financeiro, bare metal ou privacidade (pedidos do titular, LGPD art. 18).",
        "enum": [
          "sales",
          "technical",
          "billing",
          "bare_metal",
          "privacy"
        ],
        "type": "string"
      },
      "TicketExpectation": {
        "additionalProperties": false,
        "properties": {
          "due_at": {
            "format": "date-time",
            "type": "string"
          },
          "target": {
            "example": "até 4 horas úteis",
            "type": "string"
          }
        },
        "required": [
          "due_at",
          "target"
        ],
        "type": "object"
      },
      "TicketMessageInput": {
        "additionalProperties": false,
        "properties": {
          "attachment_ids": {
            "items": {
              "format": "uuid",
              "type": "string"
            },
            "maxItems": 5,
            "type": "array"
          },
          "body": {
            "maxLength": 5000,
            "type": "string"
          }
        },
        "required": [
          "body"
        ],
        "type": "object"
      },
      "TicketOpenInput": {
        "additionalProperties": false,
        "properties": {
          "attachment_ids": {
            "items": {
              "format": "uuid",
              "type": "string"
            },
            "maxItems": 5,
            "type": "array"
          },
          "category": {
            "$ref": "#/components/schemas/TicketCategory"
          },
          "message": {
            "maxLength": 5000,
            "type": "string"
          },
          "priority": {
            "$ref": "#/components/schemas/TicketPriority"
          },
          "server_id": {
            "description": "Servidor da conta de que o chamado trata.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "subject": {
            "maxLength": 150,
            "type": "string"
          }
        },
        "required": [
          "subject",
          "category",
          "message"
        ],
        "type": "object"
      },
      "TicketPriority": {
        "description": "Prioridade: baixa, normal, alta ou urgente (define o SLA).",
        "enum": [
          "low",
          "normal",
          "high",
          "urgent"
        ],
        "type": "string"
      },
      "TicketRating": {
        "additionalProperties": false,
        "properties": {
          "comment": {
            "type": "string"
          },
          "rated_at": {
            "format": "date-time",
            "type": "string"
          },
          "rating": {
            "enum": [
              "good",
              "bad"
            ],
            "type": "string"
          }
        },
        "required": [
          "rating",
          "comment",
          "rated_at"
        ],
        "type": "object"
      },
      "TicketRef": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "number": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "number"
        ],
        "type": "object"
      },
      "TicketServer": {
        "additionalProperties": false,
        "properties": {
          "hostname": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "id",
          "hostname"
        ],
        "type": "object"
      },
      "TicketStatus": {
        "description": "`open`: espera a equipe; `answered`: espera o cliente; `resolved`: resolvido.",
        "enum": [
          "open",
          "answered",
          "resolved"
        ],
        "type": "string"
      },
      "User": {
        "additionalProperties": false,
        "properties": {
          "active": {
            "type": "boolean"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "customer_id": {
            "description": "Cliente do usuário do portal; `null` para staff.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "email_verified_at": {
            "description": "Quando o usuário confirmou o e-mail (abriu um link ou digitou um código enviado a ele).",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/UserKind"
          },
          "last_login_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "pending_access": {
            "description": "`true` enquanto o usuário não ativou o acesso (cliente: não confirmou o e-mail; equipe: não criou a senha).\n",
            "type": "boolean"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "kind",
          "role",
          "customer_id",
          "name",
          "email",
          "active",
          "pending_access",
          "last_login_at",
          "created_at",
          "updated_at",
          "email_verified_at"
        ],
        "type": "object"
      },
      "UserKind": {
        "enum": [
          "staff",
          "customer"
        ],
        "type": "string"
      }
    },
    "securitySchemes": {
      "apiToken": {
        "bearerFormat": "sc_live_…",
        "description": "Token de API do cliente (`sc_live_…` em produção, `sc_test_…` nos outros ambientes), criado em\nDesenvolvedores › Tokens de API. Vale só nas operações com `x-token-scope`, e só com o escopo indicado ali e\no papel atual de quem criou o token. Limite de 120 requisições por minuto por token (rajada de 60).\n",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "contact": {
      "name": "Halver",
      "url": "https://docs.halver.com.br"
    },
    "description": "API da Halver para gerenciar servidores bare metal, chaves SSH, redes privadas, IPs elásticos, faturas e\nchamados da sua conta. Autentique com um token de API (`Authorization: Bearer sc_live_…`) criado em Desenvolvedores ›\nTokens de API no painel (dash.halver.com.br). Cada operação diz o escopo que o token precisa (`x-token-scope`; `*`\né qualquer token). Erros em `application/problem+json` (RFC 9457) com um `code` estável. Limite de 120 requisições\npor minuto por token (balde de 60), informado nos cabeçalhos `RateLimit-Policy` e `RateLimit`\n(draft-ietf-httpapi-ratelimit-headers); acima dele, 429 com `Retry-After`. Os POST que criam ou cobram aceitam\n`Idempotency-Key`. Listas com paginação por cursor (`limit`, `cursor`, `next_cursor`) e operações\nassíncronas acompanhadas em `GET /cloud/operations/{id}`. Guia: https://docs.halver.com.br/api.\n\nHalver API to manage the bare metal servers, SSH keys, private networks, elastic IPs, invoices and support tickets of\nyour account. Authenticate with an API token (`Authorization: Bearer sc_live_…`) created under Developers › API tokens\nin the panel. Each operation names the scope the token needs (`x-token-scope`). Errors are `application/problem+json`\n(RFC 9457) with a stable `code`; rate limit: 120 requests per minute per token (bucket of 60), reported in\n`RateLimit-Policy`/`RateLimit`; POSTs that create or charge accept `Idempotency-Key`; lists page with\ncursors; asynchronous operations are polled at `GET /cloud/operations/{id}`.",
    "title": "Halver API",
    "version": "1.0.0"
  },
  "openapi": "3.0.3",
  "paths": {
    "/cloud/catalog": {
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.\n\nLocais, planos com estoque por local, sistemas, se a conta está verificada (cartão salvo) e se o provisionamento está disponível.",
        "operationId": "cloudCatalog",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudCatalog"
                }
              }
            },
            "description": "Catálogo."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "O que dá para contratar agora",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      }
    },
    "/cloud/coupons/check": {
      "post": {
        "description": "Escopo do token / token scope: `servers:write`.\n\nNão resgata: mostra a primeira fatura do plano (mês cheio) com o cupom. Cupom que não\nvale, por qualquer motivo (inexistente, vencido, esgotado, já usado, de outro plano), responde 422\n`coupon.unavailable` com a mesma mensagem (\"Cupom inválido ou indisponível.\", campo coupon_code): respostas\ndiferentes diriam quais códigos existem. Limite de tentativas por conta e por IP (429 `coupon.rate_limited`).\nO cartão continua obrigatório.",
        "operationId": "cloudCheckCoupon",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudCouponCheckInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudCouponQuote"
                }
              }
            },
            "description": "Cupom válido."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Confere um cupom no checkout do deploy",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:write"
      }
    },
    "/cloud/network": {
      "get": {
        "description": "Escopo do token / token scope: `networks:read`.\n\nRegiões onde a conta tem servidores (e as outras com datacenter), com o que cada uma oferece agora (IP elástico e preço,\nfirewall, rede privada), as cotas e se a conta tem cartão salvo. Um recurso só é oferecido onde tem efeito: IPs\nelásticos e firewall pedem `routed_by_gateway` na região (o OPNsense é o gateway dos servidores) e um gateway\ncadastrado; redes privadas pedem `switch_automated` (a plataforma configura as portas do switch). IPs elásticos e\nredes privadas também pedem um bloco com espaço.",
        "operationId": "cloudNetworkOverview",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudNetworkOverview"
                }
              }
            },
            "description": "Visão da rede."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Visão da rede da conta",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:read"
      }
    },
    "/cloud/network/elastic-ips": {
      "get": {
        "description": "Escopo do token / token scope: `ips:read`.",
        "operationId": "cloudListElasticIPs",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageLimit"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudElasticIPList"
                }
              }
            },
            "description": "IPs elásticos vivos."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "IPs elásticos",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ips:read"
      },
      "post": {
        "description": "Escopo do token / token scope: `ips:write`.\n\nO próximo endereço livre dos blocos de IP flutuante da região, opcionalmente já num\nservidor da mesma região. Com preço, é cobrado no ato como um servidor: contrato com o produto\ndo bloco e a primeira fatura no cartão salvo; recusa libera o endereço e nada é cobrado (422\n`cloud.payment_refused`). Sem resposta da operadora fica `pending_payment`. O job `cloud-network`\nescreve a rota no gateway (`configuring` → `active`). Limite de 10 por conta (422 `cloud.quota_exceeded`).\nNuma região sem `routed_by_gateway` (ou sem gateway) é recusado antes de qualquer cobrança (409\n`cloud.elastic_ips_unavailable`). Aceita `Idempotency-Key`: repetir a mesma chave nunca cobra de novo.",
        "operationId": "cloudCreateElasticIP",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudElasticIPInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudElasticIP"
                }
              }
            },
            "description": "IP elástico.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Cria um IP elástico",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ips:write"
      }
    },
    "/cloud/network/elastic-ips/{id}": {
      "delete": {
        "description": "Escopo do token / token scope: `ips:write`.\n\nA rota sai do gateway (`releasing`) e o endereço volta ao bloco. O contrato termina no fim\ndo período já pago, sem reembolso.",
        "operationId": "cloudReleaseElasticIP",
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudElasticIP"
                }
              }
            },
            "description": "Liberando.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Libera o IP elástico",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ips:write"
      },
      "get": {
        "description": "Escopo do token / token scope: `ips:read`.",
        "operationId": "cloudGetElasticIP",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudElasticIP"
                }
              }
            },
            "description": "IP elástico."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha um IP elástico",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ips:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ]
    },
    "/cloud/network/elastic-ips/{id}/assign": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `ips:write`.\n\n`server_id` é um servidor da mesma região (422 `cloud.other_region` fora dela) ou `null`\n(sem servidor). Com o IP já roteado para outro servidor o status é `moving`; o job move a rota. Apontar para um\nservidor numa região sem `routed_by_gateway` é recusado (409 `cloud.elastic_ips_unavailable`); tirar do servidor\nvale em qualquer região. Aceita `Idempotency-Key`.",
        "operationId": "cloudAssignElasticIP",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudAssignInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudElasticIP"
                }
              }
            },
            "description": "Mudança em andamento.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Atribui ou move o IP elástico",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ips:write"
      }
    },
    "/cloud/network/private-networks": {
      "get": {
        "description": "Escopo do token / token scope: `networks:read`.",
        "operationId": "cloudListPrivateNetworks",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageLimit"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudPrivateNetworkList"
                }
              }
            },
            "description": "Redes privadas vivas."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Redes privadas",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:read"
      },
      "post": {
        "description": "Escopo do token / token scope: `networks:write`.\n\nUma VLAN da região (de 1000 a 3999, sem repetir VLAN de bloco ou de outra rede da região) e\numa sub-rede /24 do bloco de rede privada da região. O job define a VLAN no OPNsense (`configuring` →\n`active`). O cliente configura a VLAN (802.1Q) no sistema; o tráfego é livre e não é filtrado.\nLimite de 5 por conta. Numa região sem `switch_automated` é recusado (409 `cloud.private_networks_unavailable`).\nAceita `Idempotency-Key`.",
        "operationId": "cloudCreatePrivateNetwork",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudPrivateNetworkInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudPrivateNetwork"
                }
              }
            },
            "description": "Rede privada.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Cria uma rede privada",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:write"
      }
    },
    "/cloud/network/private-networks/{id}": {
      "delete": {
        "description": "Escopo do token / token scope: `networks:write`.\n\nSó sem servidores (409 `cloud.private_network_in_use`); a VLAN sai do gateway e a VLAN e a sub-rede ficam livres.",
        "operationId": "cloudDeletePrivateNetwork",
        "responses": {
          "204": {
            "description": "Apagando."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Apaga a rede privada",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:write"
      },
      "get": {
        "description": "Escopo do token / token scope: `networks:read`.",
        "operationId": "cloudGetPrivateNetwork",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudPrivateNetwork"
                }
              }
            },
            "description": "Rede privada."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha uma rede privada",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "put": {
        "description": "Escopo do token / token scope: `networks:write`.",
        "operationId": "cloudUpdatePrivateNetwork",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudPrivateNetworkUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudPrivateNetwork"
                }
              }
            },
            "description": "Rede privada."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Altera a descrição",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:write"
      }
    },
    "/cloud/network/private-networks/{id}/servers": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `networks:write`.\n\nSó servidores da mesma região (422 `cloud.other_region`), numa região com\n`switch_automated` (409 `cloud.private_networks_unavailable` fora dela). O servidor recebe um endereço sugerido\nda sub-rede e o job `cloud-network` leva a VLAN à porta do switch dele (`connecting` → `active` ou `failed`,\ncom a resposta do switch em `apply_error`). Colocar de novo um servidor `failed` tenta outra vez. Aceita\n`Idempotency-Key`.",
        "operationId": "cloudJoinPrivateNetwork",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudServerRef"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudPrivateNetwork"
                }
              }
            },
            "description": "Rede privada."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Coloca um servidor na rede",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:write"
      }
    },
    "/cloud/network/private-networks/{id}/servers/{serverID}": {
      "delete": {
        "description": "Escopo do token / token scope: `networks:write`.\n\nFica `removing` até o job tirar a VLAN da porta do switch e então sai. Numa região sem\n`switch_automated` sai na hora (a plataforma não gravou nada no switch).",
        "operationId": "cloudLeavePrivateNetwork",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudPrivateNetwork"
                }
              }
            },
            "description": "Rede privada."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Tira um servidor da rede",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "networks:write"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "$ref": "#/components/parameters/ServerId"
        }
      ]
    },
    "/cloud/operations": {
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.\n\nDeploys, reinstalações, apagamentos e remoções, na ordem em que foram pedidos; `server_id` filtra um servidor,\n`external_id` e `end_customer_ref` os servidores com essas referências (as atuais de cada servidor).\nAs operações de um servidor removido continuam legíveis. Paginação por cursor opcional.",
        "operationId": "cloudListOperations",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageLimit"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          },
          {
            "$ref": "#/components/parameters/ExternalIdQuery"
          },
          {
            "$ref": "#/components/parameters/EndCustomerRefQuery"
          },
          {
            "in": "query",
            "name": "server_id",
            "required": false,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudOperationList"
                }
              }
            },
            "description": "Operações."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Operações dos servidores da conta",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      }
    },
    "/cloud/operations/{id}": {
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.\n\nO contrato de espera: consulte até `status` ser `succeeded` ou `failed`, respeitando `Retry-After` enquanto\n`pending` ou `running`.",
        "operationId": "cloudGetOperation",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudOperation"
                }
              }
            },
            "description": "Operação.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha uma operação",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ]
    },
    "/cloud/servers": {
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.\n\nFiltros: `status` (um ou mais, separados por vírgula), `site_id`, `plan_id`, `hostname_prefix`, `tag`,\n`external_id` e `end_customer_ref`, aplicados no banco. Paginação por cursor opcional (`limit`, `cursor`).",
        "operationId": "cloudListServers",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageLimit"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          },
          {
            "$ref": "#/components/parameters/ExternalIdQuery"
          },
          {
            "$ref": "#/components/parameters/EndCustomerRefQuery"
          },
          {
            "description": "pending_payment, deploying, active, failed, wiping, empty ou deleting; vários separados por vírgula.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "site_id",
            "required": false,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "plan_id",
            "required": false,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "hostname_prefix",
            "required": false,
            "schema": {
              "maxLength": 63,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "tag",
            "required": false,
            "schema": {
              "maxLength": 64,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServerList"
                }
              }
            },
            "description": "Servidores."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Servidores da conta",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      },
      "post": {
        "description": "Escopo do token / token scope: `servers:write`.\n\nExige cartão salvo, menos para conta de uso interno. Reserva uma\nmáquina em estoque, abre o contrato mensal do servidor (com o cupom, se houver) e registra a operação, que segue\nem segundo plano (acompanhe `GET /cloud/operations/{operation_id}`). Primeira fatura sem nada a pagar\n(conta interna, cupom de 100%) não passa pelo cartão. Cupom que não vale responde 422 `coupon.unavailable`\n(\"Cupom inválido ou indisponível.\", sem o motivo) e desfaz a compra; com cupom, o pedido conta no limite de\ntentativas de cupom (429 `coupon.rate_limited`). Aceita `Idempotency-Key`: repetir a mesma chave nunca\ncobra nem cria um segundo servidor. `external_id` (o pedido do revendedor) é único na conta entre os servidores não\nremovidos: um segundo deploy com o mesmo valor responde 409 `cloud.external_id_taken` com o id do servidor que já o\ntem em `existing_id`, sem reservar máquina nem cobrar; a mesma `Idempotency-Key` continua devolvendo a resposta\nguardada do primeiro pedido.",
        "operationId": "cloudDeployServer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudDeployInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Servidor em deploy.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Faz o deploy de um servidor",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:write"
      }
    },
    "/cloud/servers/{id}": {
      "delete": {
        "description": "Escopo do token / token scope: `servers:delete`.\n\n`confirm` deve ser o hostname. O\ncancelamento vale no fim do período já pago (`cancel_at`, o último dia): não há nova fatura nem reembolso,\no cliente segue usando a máquina e pode desfazer em `DELETE /cloud/servers/{id}/cancellation`. Depois desse\ndia o contrato termina, os discos são apagados e a máquina volta ao estoque. Para só apagar o\nsistema e manter a máquina, use `POST /cloud/servers/{id}/wipe`.\n\n`release=immediate` (para automação que precisa da máquina livre na hora) remove o servidor **agora**: o contrato\ntermina hoje, os discos são apagados e a máquina volta ao estoque, **sem reembolso** do período já pago (as faturas\nde períodos ainda não iniciados são canceladas). Fatura em aberto do período em uso recusa (422\n`cloud.unpaid_invoices`): a liberação não perdoa dívida. Desligado por padrão: 422\n`cloud.immediate_release_disabled` quando a plataforma não a libera. Acompanhe a remoção\npor `GET /cloud/operations/{operation_id}`; ao terminar, `GET /cloud/servers/{id}` responde 404.",
        "operationId": "cloudDeleteServer",
        "parameters": [
          {
            "description": "`end_of_period` (padrão) ou `immediate` (sem reembolso).",
            "in": "query",
            "name": "release",
            "required": false,
            "schema": {
              "enum": [
                "end_of_period",
                "immediate"
              ],
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "confirm": {
                    "type": "string"
                  }
                },
                "required": [
                  "confirm"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Cancelamento agendado (padrão) ou remoção em andamento (`release=immediate`, status `deleting`).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Cancela a assinatura do servidor",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:delete"
      },
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.",
        "operationId": "cloudGetServer",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Servidor com as etapas da operação atual."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha um servidor",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "patch": {
        "description": "Escopo do token / token scope: `servers:write`.\n\nSó rótulos, em qualquer status menos removido, sem tocar na máquina: o hostname\nmuda na plataforma e o sistema mantém o da instalação até a próxima reinstalação. Tags filtram a lista (`tag`).\n`external_id` que outro servidor da conta já tem responde 409 `cloud.external_id_taken` com o id dele em\n`existing_id`.",
        "operationId": "cloudUpdateServer",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudServerUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Servidor."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Altera hostname, tags, descrição e as referências do revendedor",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:write"
      }
    },
    "/cloud/servers/{id}/boot-device": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `servers:write`.\n\nSó o próximo boot: `disk` os discos ou `pxe` a rede de instalação (num\nservidor com sistema ela volta aos discos). O Setup do BIOS é protegido por senha e não é oferecido; uma\nmudança no BIOS se pede por chamado. Não reinicia. Com o servidor ativo, ou vazio com \"Sem\nsistema\"; 409 nos demais estados. Mídia virtual não é oferecida (licença da IPMI). Pelo servidor MCP só depois da\naprovação da pessoa no portal.",
        "operationId": "cloudSetBootDevice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "device": {
                    "enum": [
                      "pxe",
                      "disk"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "device"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Servidor."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Dispositivo do próximo boot",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:write"
      }
    },
    "/cloud/servers/{id}/cancellation": {
      "delete": {
        "description": "Escopo do token / token scope: `servers:write`.\n\nA assinatura segue e volta a ser cobrada no próximo período.",
        "operationId": "cloudUndoCancelServer",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Assinatura mantida."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Desfaz o cancelamento agendado",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:write"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ]
    },
    "/cloud/servers/{id}/console": {
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.\n\nSe o servidor pode abrir uma sessão do console serial (IPMI SOL; `unavailable` diz por que não), o tempo sem\nuso e o máximo de uma sessão, as teclas nomeadas, a cota da conta, a sessão viva (`live`) e as 10 últimas encerradas.",
        "operationId": "cloudConsole",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudConsoleOverview"
                }
              }
            },
            "description": "Console serial do servidor."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Console serial do servidor",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nAcesso de teclado ao servidor pelo SOL da IPMI, aberto pelo broker do motor (as credenciais\nda IPMI nunca saem do motor). Com o servidor em repouso (ativo, sem sistema ou falho) e fora de suspensão (422\n`cloud.console_unavailable`); uma sessão viva por servidor e por máquina (409 `cloud.console_open`) e 5 por conta (422\n`cloud.quota_exceeded`). Fecha com 15 minutos sem uso (ler a tela ou digitar), em 2 horas, quando o cliente ou a equipe\nencerra e antes de qualquer operação nossa no servidor. Pelo servidor MCP só depois da aprovação da pessoa no portal\n(403 sem ela). 503 `cloud.console_failed` quando o SOL não abre.",
        "operationId": "cloudOpenConsole",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudConsoleSession"
                }
              }
            },
            "description": "Sessão aberta."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Abre o console serial",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      }
    },
    "/cloud/servers/{id}/console/{sessionID}": {
      "delete": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nO acesso termina na hora; a sessão do broker fecha e a gravação é guardada (`closed`), ou o\njob `cloud-console` termina isso (`closing`). Repetir numa sessão encerrada devolve a sessão como está.",
        "operationId": "cloudEndConsole",
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudConsoleSession"
                }
              }
            },
            "description": "Encerrando ou encerrada."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Encerra o console serial",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      },
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.",
        "operationId": "cloudGetConsole",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudConsoleSession"
                }
              }
            },
            "description": "Sessão."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha uma sessão do console serial",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "in": "path",
          "name": "sessionID",
          "required": true,
          "schema": {
            "format": "uuid",
            "type": "string"
          }
        }
      ]
    },
    "/cloud/servers/{id}/console/{sessionID}/input": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "in": "path",
          "name": "sessionID",
          "required": true,
          "schema": {
            "format": "uuid",
            "type": "string"
          }
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nTextos e teclas nomeadas, na ordem (até 64 itens e 4096 bytes). Não há Ctrl-Alt-Del nem\nbreak serial: reset e energia nunca passam pelo console. Toda entrada é registrada com quem digitou (usuário, token de\nAPI ou cliente OAuth pelo MCP; o texto guardado cifrado). Com `wait_for`, espera esse texto na tela por até\n`timeout_seconds` (máx. 25) e diz em `matched` se apareceu. 429 `cloud.console_rate_limited` acima de 5 envios por\nsegundo (rajada de 30).",
        "operationId": "cloudConsoleInput",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudConsoleInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudConsoleScreen"
                }
              }
            },
            "description": "A tela depois da entrada."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Digita no console serial",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      }
    },
    "/cloud/servers/{id}/console/{sessionID}/output": {
      "get": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nOs bytes que o servidor mandou a partir de `offset` (até 64 KiB, em base64), para um\nemulador de terminal no navegador. `reset` diz que o pedido estava fora do que o broker guarda (1 MiB) e a resposta\ncomeça no que ainda há. Conta como uso da sessão.",
        "operationId": "cloudConsoleOutput",
        "parameters": [
          {
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudConsoleOutput"
                }
              }
            },
            "description": "Saída."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Saída bruta do console serial",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "in": "path",
          "name": "sessionID",
          "required": true,
          "schema": {
            "format": "uuid",
            "type": "string"
          }
        }
      ]
    },
    "/cloud/servers/{id}/console/{sessionID}/screen": {
      "get": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nA tela que o emulador de terminal do broker mostra (linhas de texto e cursor). Conta como uso\nda sessão. É saída do servidor: dado não confiável. 409 `cloud.console_closed` com a sessão fora do ar.",
        "operationId": "cloudConsoleScreen",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudConsoleScreen"
                }
              }
            },
            "description": "Tela."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Tela do console serial",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "in": "path",
          "name": "sessionID",
          "required": true,
          "schema": {
            "format": "uuid",
            "type": "string"
          }
        }
      ]
    },
    "/cloud/servers/{id}/console/{sessionID}/transcript": {
      "get": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nA gravação da sessão encerrada (asciicast v2: saída e entrada com o tempo de cada uma), como\narquivo; toca no asciinema. 404 enquanto não foi guardada ou quando o broker não a tinha.",
        "operationId": "cloudConsoleTranscript",
        "responses": {
          "200": {
            "content": {
              "application/x-asciicast": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Gravação (asciicast v2)."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Gravação da sessão do console serial",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "in": "path",
          "name": "sessionID",
          "required": true,
          "schema": {
            "format": "uuid",
            "type": "string"
          }
        }
      ]
    },
    "/cloud/servers/{id}/ipmi-vpn": {
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.\n\nSe o servidor pode abrir uma sessão de VPN até a IPMI (`unavailable` diz por que não), as durações, a cota da conta, as\nportas liberadas e as sessões do servidor (as vivas e as 5 últimas encerradas). A senha da conta da IPMI vem enquanto a\nsessão está viva.",
        "operationId": "cloudIPMIVPN",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudIPMIVPNOverview"
                }
              }
            },
            "description": "IPMI do servidor."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "IPMI do servidor (VPN)",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nCom o servidor em repouso (ativo, sem sistema ou falho) e na região com VPN (422\n`cloud.vpn_unavailable`); indisponível enquanto uma operação nossa roda (deploy, reinstalação, limpeza, exclusão) e\nenquanto o servidor está desligado por falta de pagamento (409 `cloud.server_suspended`; o desligamento encerra as\nsessões abertas antes de desligar).\n`public_key` é a\nchave pública WireGuard gerada no dispositivo do cliente: a chave privada nunca chega à plataforma. A sessão nasce\n`opening` com o endereço do túnel e a conta da IPMI (usuário e senha gerados, papel Operator); o job `cloud-ipmi-vpn`\ncria a conta na IPMI e o peer no OPNsense (`active`, com a chave pública do OPNsense e o arquivo do WireGuard) e tira\ntudo ao vencer. Uma sessão viva por servidor (409 `cloud.vpn_session_open`) e 5 por conta (422 `cloud.quota_exceeded`).\nPor token de API só com o escopo `servers:console`, e pelo servidor MCP só depois da aprovação da pessoa no portal; a\nsenha da IPMI nunca volta ao MCP (a pessoa a lê no portal).",
        "operationId": "cloudOpenIPMIVPN",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudIPMIVPNInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudIPMIVPNSession"
                }
              }
            },
            "description": "Sessão abrindo."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Abre uma sessão de VPN até a IPMI",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      }
    },
    "/cloud/servers/{id}/ipmi-vpn/{sessionID}": {
      "delete": {
        "description": "Escopo do token / token scope: `servers:console`.\n\nA sessão passa a `closing` (motivo `ended`); o job tira as regras e o peer do OPNsense e\ndepois a conta da IPMI (`closed`). Repetir numa sessão já encerrada devolve a sessão como está.",
        "operationId": "cloudEndIPMIVPN",
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudIPMIVPNSession"
                }
              }
            },
            "description": "Encerrando."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Encerra a sessão antes do fim",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:console"
      },
      "get": {
        "description": "Escopo do token / token scope: `servers:read`.",
        "operationId": "cloudGetIPMIVPN",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudIPMIVPNSession"
                }
              }
            },
            "description": "Sessão."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha uma sessão de VPN até a IPMI",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "in": "path",
          "name": "sessionID",
          "required": true,
          "schema": {
            "format": "uuid",
            "type": "string"
          }
        }
      ]
    },
    "/cloud/servers/{id}/power": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `servers:write`.\n\nCom o servidor ativo, ou vazio com \"Sem sistema\" (o sistema próprio do cliente, instalado pela IPMI); 409 nos demais estados. Aceita `Idempotency-Key`.",
        "operationId": "cloudPowerServer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "action": {
                    "enum": [
                      "on",
                      "off",
                      "reboot"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "action"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Servidor.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Liga, desliga ou reinicia",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:write"
      }
    },
    "/cloud/servers/{id}/reinstall": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `servers:delete`.\n\nApaga os discos e instala de novo; sistema, chaves e user data opcionais. `user_data`\nausente mantém o do servidor (roda de novo), vazio remove e um documento novo substitui. Aceita\n`Idempotency-Key`.",
        "operationId": "cloudReinstallServer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "disk_layout": {
                    "description": "Legado (use storage, que tem precedência): single é o disco padrão (ext4 no Proxmox VE) e raid1 o espelho do primeiro par de discos iguais (ZFS RAID 1 no Proxmox VE). Sem storage nem disk_layout, a reinstalação mantém o armazenamento da última quando o novo sistema o aceita como está, senão usa o padrão do sistema.",
                    "enum": [
                      "single",
                      "raid1",
                      null
                    ],
                    "nullable": true,
                    "type": "string"
                  },
                  "os_image_id": {
                    "format": "uuid",
                    "nullable": true,
                    "type": "string"
                  },
                  "password": {
                    "description": "Gera uma nova senha (enviada por e-mail); ausente mantém a escolha da última instalação.",
                    "nullable": true,
                    "type": "boolean"
                  },
                  "ssh_key_ids": {
                    "items": {
                      "format": "uuid",
                      "type": "string"
                    },
                    "nullable": true,
                    "type": "array"
                  },
                  "storage": {
                    "$ref": "#/components/schemas/CloudStorageInput"
                  },
                  "user_data": {
                    "description": "Ausente ou nulo mantém o user data do servidor; vazio remove; um documento substitui (regras do deploy).",
                    "maxLength": 90000,
                    "nullable": true,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Servidor em reinstalação.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Reinstala um servidor",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:delete"
      }
    },
    "/cloud/servers/{id}/wipe": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `servers:delete`.\n\n`confirm` deve ser o hostname. Os discos são apagados e o servidor fica\n`empty` (sem sistema): a máquina continua reservada para o cliente e a assinatura segue.\nUm novo deploy usa `POST /cloud/servers/{id}/reinstall`. Aceita `Idempotency-Key`.",
        "operationId": "cloudWipeServer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "confirm": {
                    "type": "string"
                  }
                },
                "required": [
                  "confirm"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudServer"
                }
              }
            },
            "description": "Apagando o sistema.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Retry-After": {
                "$ref": "#/components/headers/PollAfter"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Apaga o sistema e os dados, mantendo a máquina",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "servers:delete"
      }
    },
    "/cloud/ssh-keys": {
      "get": {
        "description": "Escopo do token / token scope: `ssh_keys:read`.",
        "operationId": "cloudListSSHKeys",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageLimit"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSSHKeyList"
                }
              }
            },
            "description": "Chaves."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Chaves SSH da conta",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ssh_keys:read"
      },
      "post": {
        "description": "Escopo do token / token scope: `ssh_keys:write`.\n\ned25519, ECDSA ou RSA de 2048 bits ou mais; uma linha de authorized_keys sem opções. Aceita `Idempotency-Key`.",
        "operationId": "cloudAddSSHKey",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudSSHKeyInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSSHKey"
                }
              }
            },
            "description": "Chave criada.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Adiciona uma chave SSH",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ssh_keys:write"
      }
    },
    "/cloud/ssh-keys/{id}": {
      "delete": {
        "description": "Escopo do token / token scope: `ssh_keys:write`.\n\nServidores já instalados mantêm a chave até um reinstall.",
        "operationId": "cloudDeleteSSHKey",
        "responses": {
          "204": {
            "description": "Removida."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Remove uma chave SSH",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ssh_keys:write"
      },
      "get": {
        "description": "Escopo do token / token scope: `ssh_keys:read`.",
        "operationId": "cloudGetSSHKey",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSSHKey"
                }
              }
            },
            "description": "Chave."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha uma chave SSH",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ssh_keys:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "patch": {
        "description": "Escopo do token / token scope: `ssh_keys:write`.\n\nSó o nome muda; a chave em si nunca (cadastre outra).",
        "operationId": "cloudRenameSSHKey",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudSSHKeyUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSSHKey"
                }
              }
            },
            "description": "Chave."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Renomeia uma chave SSH",
        "tags": [
          "cloud"
        ],
        "x-token-scope": "ssh_keys:write"
      }
    },
    "/me": {
      "get": {
        "description": "Escopo do token / token scope: qualquer token / any token.",
        "operationId": "getMe",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            },
            "description": "Usuário autenticado."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Usuário atual e permissões",
        "tags": [
          "me"
        ],
        "x-token-scope": "*"
      }
    },
    "/portal/invoices": {
      "get": {
        "description": "Escopo do token / token scope: `invoices:read`.\n\nVisão do cliente (sem dados internos). As cobranças não são incluídas. `external_id` e `end_customer_ref`\ntrazem as faturas dos contratos dos servidores com essa referência (a atual de cada servidor, removidos inclusive).",
        "operationId": "portalListInvoices",
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/InvoiceStatus"
            }
          },
          {
            "$ref": "#/components/parameters/Search"
          },
          {
            "$ref": "#/components/parameters/ContractIdQuery"
          },
          {
            "$ref": "#/components/parameters/ExternalIdQuery"
          },
          {
            "$ref": "#/components/parameters/EndCustomerRefQuery"
          },
          {
            "$ref": "#/components/parameters/DueFrom"
          },
          {
            "$ref": "#/components/parameters/DueTo"
          },
          {
            "$ref": "#/components/parameters/Competence"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalInvoicePage"
                }
              }
            },
            "description": "Página de faturas."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Lista as faturas do cliente",
        "tags": [
          "portal"
        ],
        "x-token-scope": "invoices:read"
      }
    },
    "/portal/invoices/{id}": {
      "get": {
        "description": "Escopo do token / token scope: `invoices:read`.\n\nVisão do cliente; cobranças canceladas ou recusadas não são listadas.",
        "operationId": "portalGetInvoice",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalInvoice"
                }
              }
            },
            "description": "Fatura."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Detalha uma fatura do cliente com cobranças",
        "tags": [
          "portal"
        ],
        "x-token-scope": "invoices:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ]
    },
    "/portal/invoices/{id}/receipt": {
      "get": {
        "description": "Escopo do token / token scope: `invoices:read`.\n\nEmite o comprovante no primeiro acesso. Fatura não paga: 422 `invoice.not_paid`.",
        "operationId": "portalGetReceipt",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                }
              }
            },
            "description": "Comprovante."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Comprovante de pagamento de uma fatura do cliente",
        "tags": [
          "portal"
        ],
        "x-token-scope": "invoices:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ]
    },
    "/portal/payment-methods": {
      "get": {
        "description": "Escopo do token / token scope: `payment_methods:read`.",
        "operationId": "portalListPaymentMethods",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentMethodList"
                }
              }
            },
            "description": "Cartões salvos (padrão primeiro)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Lista os cartões salvos",
        "tags": [
          "portal"
        ],
        "x-token-scope": "payment_methods:read"
      }
    },
    "/portal/support/tickets": {
      "get": {
        "description": "Escopo do token / token scope: `support:read`.\n\nChamados da conta do usuário, da conversa mais recente para a mais antiga, opcionalmente de uma ou mais\nsituações. Sem `limit` nem `offset` vêm os 200 mais recentes; `meta.total` é o total da situação pedida e `counts`\nconta os chamados em andamento e resolvidos da conta. `unread` diz que há resposta da equipe ainda não lida;\n`expected` é quando a equipe deve responder. Notas internas, nomes da equipe, grupo, etiquetas e o responsável nunca\naparecem no portal.",
        "operationId": "portalListTickets",
        "parameters": [
          {
            "description": "Uma ou mais situações (`open`, `answered`, `resolved`) separadas por vírgula; `open,answered` são os em andamento.",
            "in": "query",
            "name": "status",
            "schema": {
              "example": "open,answered",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalTicketList"
                }
              }
            },
            "description": "Chamados."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Lista os chamados da conta",
        "tags": [
          "support"
        ],
        "x-token-scope": "support:read"
      },
      "post": {
        "description": "Escopo do token / token scope: `support:write`.\n\nAbre o chamado com a primeira mensagem;\n`server_id` é um servidor da própria conta (outro servidor: 400 no campo). A equipe recebe a notificação no sino\ne, se configurado, um aviso por e-mail sem o conteúdo. Rate limit por conta: 10 por dia (burst 5).",
        "operationId": "portalOpenTicket",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TicketOpenInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalTicket"
                }
              }
            },
            "description": "Chamado aberto, com a conversa."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Abre um chamado",
        "tags": [
          "support"
        ],
        "x-token-scope": "support:write"
      }
    },
    "/portal/support/tickets/{id}": {
      "get": {
        "description": "Escopo do token / token scope: `support:read`.\n\nAbrir o chamado marca as respostas da equipe como lidas. Chamado de outra conta: 404. Depois do prazo de guarda\n(1 ano após a resolução) a conversa vem vazia e `purged` é verdadeiro.",
        "operationId": "portalGetTicket",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalTicket"
                }
              }
            },
            "description": "Chamado."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Mostra um chamado com a conversa",
        "tags": [
          "support"
        ],
        "x-token-scope": "support:read"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ]
    },
    "/portal/support/tickets/{id}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `support:write`.\n\nO chamado volta a esperar a equipe; o resolvido reabre. Num chamado\nfechado (resolvido há mais dias que `close_days`) a mensagem abre um chamado novo de continuação, que é o\ndevolvido (outro `id`, com `follow_up_of`). Chamado juntado a outro: 409 `support.ticket_merged`. `internal` e\n`macro_id` só valem para a equipe (400). `attachment_ids`: anexos enviados antes por `/portal/support/attachments`.\nConversa apagada pelo prazo de guarda ou com 200 mensagens: 409. Rate limit por conta: 60 por hora (burst 20).",
        "operationId": "portalReplyTicket",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TicketMessageInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalTicket"
                }
              }
            },
            "description": "Mensagem gravada; o chamado com a conversa."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Responde no chamado",
        "tags": [
          "support"
        ],
        "x-token-scope": "support:write"
      }
    },
    "/portal/support/tickets/{id}/resolve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "description": "Escopo do token / token scope: `support:write`.\n\nDe novo não muda nada; uma mensagem nova reabre.",
        "operationId": "portalResolveTicket",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalTicket"
                }
              }
            },
            "description": "Chamado."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "apiToken": []
          }
        ],
        "summary": "Marca o chamado como resolvido",
        "tags": [
          "support"
        ],
        "x-token-scope": "support:write"
      }
    }
  },
  "security": [
    {
      "apiToken": []
    }
  ],
  "servers": [
    {
      "url": "https://dash.halver.com.br/api/v1"
    }
  ],
  "tags": [
    {
      "description": "Portal do cliente final (servidores bare metal, chaves SSH e rede - IPs elásticos, redes privadas e firewalls), restrito à conta do usuário autenticado.",
      "name": "cloud"
    },
    {
      "description": "Usuário autenticado.",
      "name": "me"
    },
    {
      "description": "Portal do cliente (sempre restrito ao cliente do usuário autenticado).",
      "name": "portal"
    },
    {
      "description": "Chamados de suporte: o cliente abre e conversa na área do cliente (\"Suporte\"); a equipe responde no admin\n(\"Atendimento\"), com notas internas, responsável e o contexto da conta.\n",
      "name": "support"
    }
  ]
}
