{
  "openapi": "3.1.0",
  "info": {
    "title": "AtendroZAP — integração Atendro",
    "version": "2026-09-17",
    "description": "Contrato auditado contra o router deste repositório. Servidor em fase laboratory. Testes HTTP usam stores/sessões sintéticos; não equivalem a homologação no WhatsApp real. Consultar ATENDRO_INTEGRATION.md, API_ENDPOINTS.md e PILOT_GATES.md. Nenhuma credencial real é incluída. /metrics tem listener próprio e não faz parte desta base URL. Webhook global configurável por servidor em /v1/webhook, com herança e exceções por instância. Documentação central em https://wpp.atendro.cloud/docs; OpenAPI e llms.txt no mesmo domínio. As chamadas REST usam a URL e as credenciais do servidor selecionado."
  },
  "externalDocs": {
    "description": "Documentação central de integração do AtendroZAP",
    "url": "https://wpp.atendro.cloud/docs"
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8091",
      "description": "Padrão local; usar somente ambiente descartável para testes."
    },
    {
      "url": "https://1.atendro.cloud",
      "description": "Servidor 1; readiness verificada em 17/09/2026. Não executar mutações/pareamento sem autorização explícita."
    }
  ],
  "tags": [
    {
      "name": "Diagnóstico"
    },
    {
      "name": "Instâncias"
    },
    {
      "name": "Administração"
    },
    {
      "name": "Mensagens"
    },
    {
      "name": "Consultas"
    },
    {
      "name": "Mídia"
    },
    {
      "name": "Leitura"
    },
    {
      "name": "Grupos"
    },
    {
      "name": "Proxy"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Ligações"
    }
  ],
  "paths": {
    "/health/live": {
      "get": {
        "operationId": "healthLive",
        "tags": [
          "Diagnóstico"
        ],
        "summary": "Verificar processo HTTP",
        "description": "Sem autenticação; não atesta banco nem WhatsApp.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Verificar processo HTTP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestLivenessAndReadiness"
        ]
      }
    },
    "/health/ready": {
      "get": {
        "operationId": "healthReady",
        "tags": [
          "Diagnóstico"
        ],
        "summary": "Verificar banco acessível",
        "description": "200 não significa instância conectada nem integração homologada.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Verificar banco acessível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "description": "Banco inacessível: status=not_ready, reason=database_unreachable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestLivenessAndReadiness"
        ]
      }
    },
    "/v1/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "tags": [
          "Diagnóstico"
        ],
        "summary": "Consultar capacidades",
        "description": "Fonte para habilitar funcionalidades. Para voz, exigir capabilities.calls=true e instance_kinds contendo calls. status=laboratory.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar capacidades",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capabilities"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCapabilitiesReportLaboratoryScope",
          "TestInitCreatesCallsInstances"
        ]
      }
    },
    "/instance/init": {
      "post": {
        "operationId": "createInstance",
        "tags": [
          "Instâncias"
        ],
        "summary": "Criar instância e emitir token",
        "description": "Não pareia nem conecta. Guardar instance.id e instance.token no backend; listar não recupera o token. kind=whatsapp por padrão; kind=calls cria um dispositivo vinculado próprio para voz.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Criar instância e emitir token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InitResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "invalid_name, invalid_kind ou invalid_json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported ao solicitar kind=calls com o módulo desligado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestInitCreatesInstanceAndReturnsTokenOnce",
          "TestInitValidatesNames",
          "TestIdempotentSendAndInit",
          "TestInitCreatesCallsInstances"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InitRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 900
      }
    },
    "/instance/all": {
      "get": {
        "operationId": "listInstances",
        "tags": [
          "Instâncias"
        ],
        "summary": "Listar instâncias do servidor",
        "description": "Array direto. Nunca inclui token; qrcode é vazio. companyId é filtro administrativo, não isolamento de autenticação.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "companyId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Listar instâncias do servidor",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Instance"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestInitCreatesInstanceAndReturnsTokenOnce"
        ]
      }
    },
    "/instance/connect": {
      "post": {
        "operationId": "connectInstance",
        "tags": [
          "Instâncias"
        ],
        "summary": "Conectar ou iniciar pareamento",
        "description": "Abre conexão real; exige autorização para laboratório. Aguarda até 5 s por QR/login; acompanhar /instance/status. Sem phone: QR; com phone: paircode.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Conectar ou iniciar pareamento",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestConnectReturnsRenderedQRCode",
          "TestConnectValidatesPairingPhone"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectRequest"
              }
            }
          }
        }
      }
    },
    "/instance/status": {
      "get": {
        "operationId": "getInstanceStatus",
        "tags": [
          "Instâncias"
        ],
        "summary": "Consultar sessão e webhook",
        "description": "Sem iniciar conexão. Usar connected e loggedIn; QR/código podem estar presentes durante pareamento.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar sessão e webhook",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestStatusReportsConnectedSession",
          "TestStatusExposesDisconnectCodeAndLimits"
        ]
      }
    },
    "/instance/disconnect": {
      "post": {
        "operationId": "disconnectInstance",
        "tags": [
          "Instâncias"
        ],
        "summary": "Desvincular e limpar credencial",
        "description": "Logout, distinto de restart. unlinked informa confirmação remota.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Desvincular e limpar credencial",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisconnectResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestDisconnectRestartAndDelete"
        ]
      }
    },
    "/instance/restart": {
      "post": {
        "operationId": "restartInstance",
        "tags": [
          "Instâncias"
        ],
        "summary": "Reiniciar preservando credencial",
        "description": "Reabre sessão; não usar como rotina de validação de cadastro.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Reiniciar preservando credencial",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RestartResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestDisconnectRestartAndDelete"
        ]
      }
    },
    "/instance": {
      "delete": {
        "operationId": "deleteInstance",
        "tags": [
          "Instâncias"
        ],
        "summary": "Excluir instância",
        "description": "Logout em melhor esforço e exclusão definitiva; somente após autorização do responsável.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Excluir instância",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseMessage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestDisconnectRestartAndDelete"
        ]
      }
    },
    "/v1/instances/{id}": {
      "get": {
        "operationId": "adminGetInstance",
        "tags": [
          "Administração"
        ],
        "summary": "Consultar instância por ID",
        "description": "Ação administrativa com admintoken, sem precisar do token da instância. Mesmos efeitos da rota de instância.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar instância por ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestAdminRoutesByInstanceID"
        ]
      },
      "delete": {
        "operationId": "adminDeleteInstance",
        "tags": [
          "Administração"
        ],
        "summary": "Excluir instância por ID",
        "description": "Ação administrativa com admintoken, sem precisar do token da instância. Mesmos efeitos da rota de instância.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Excluir instância por ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseMessage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestAdminRoutesByInstanceID"
        ]
      }
    },
    "/v1/instances/{id}/connect": {
      "post": {
        "operationId": "adminConnectInstance",
        "tags": [
          "Administração"
        ],
        "summary": "Conectar instância por ID",
        "description": "Ação administrativa com admintoken, sem precisar do token da instância. Mesmos efeitos da rota de instância.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Conectar instância por ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestAdminRoutesByInstanceID"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectRequest"
              }
            }
          }
        }
      }
    },
    "/v1/instances/{id}/disconnect": {
      "post": {
        "operationId": "adminDisconnectInstance",
        "tags": [
          "Administração"
        ],
        "summary": "Desvincular instância por ID",
        "description": "Ação administrativa com admintoken, sem precisar do token da instância. Mesmos efeitos da rota de instância.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Desvincular instância por ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisconnectResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestAdminRoutesByInstanceID"
        ]
      }
    },
    "/v1/instances/{id}/restart": {
      "post": {
        "operationId": "adminRestartInstance",
        "tags": [
          "Administração"
        ],
        "summary": "Reiniciar instância por ID",
        "description": "Ação administrativa com admintoken, sem precisar do token da instância. Mesmos efeitos da rota de instância.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Reiniciar instância por ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RestartResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestAdminRoutesByInstanceID"
        ]
      }
    },
    "/v1/instances/{id}/token": {
      "post": {
        "operationId": "adminReissueToken",
        "tags": [
          "Administração"
        ],
        "summary": "Reemitir token da instância",
        "description": "O novo segredo aparece nesta resposta. graceSeconds=0 revoga o anterior imediatamente; máximo 3600. Não possui idempotência.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Reemitir token da instância",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "whatsapp_unreachable ou storage_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Corpo ou telefone inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "session_owned_elsewhere ou outro conflito de sessão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "worker_full; Retry-After=5.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestTokenReissueByInstanceID"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            }
          }
        }
      }
    },
    "/admin/restart": {
      "post": {
        "operationId": "adminRestartWorker",
        "tags": [
          "Administração"
        ],
        "summary": "Reiniciar sessões deste worker",
        "description": "Soft restart das sessões do worker que atende a requisição. Não reinicia processo nem todos os workers do cluster.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "202": {
            "description": "Reiniciar sessões deste worker",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminRestartResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestChatFindAndAdminRoutes"
        ]
      }
    },
    "/send/text": {
      "post": {
        "operationId": "sendText",
        "tags": [
          "Mensagens"
        ],
        "summary": "Enviar texto",
        "description": "Retorna ID real do WhatsApp, não ID de fila. linkPreview é ignorado.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Enviar texto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestSendTextReturnsRealMessageID",
          "TestSendTextMapsSessionErrors",
          "TestIdempotentSendAndInit"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendTextRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/send/media": {
      "post": {
        "operationId": "sendMedia",
        "tags": [
          "Mensagens"
        ],
        "summary": "Enviar mídia",
        "description": "URL depende da allowlist de mídia. PTT requer OGG/Opus; sem transcodificação outros formatos saem como áudio comum.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Enviar mídia",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendMediaResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestSendMediaValidatesAndForwards",
          "TestSendMediaFromAllowlistedURL"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMediaRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/send/contact": {
      "post": {
        "operationId": "sendContact",
        "tags": [
          "Mensagens"
        ],
        "summary": "Enviar contato",
        "description": "Contato vCard; number é o destinatário e phoneNumber é o telefone do contato.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Enviar contato",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestSendContactAndLocation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendContactRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/send/location": {
      "post": {
        "operationId": "sendLocation",
        "tags": [
          "Mensagens"
        ],
        "summary": "Enviar localização",
        "description": "Coordenadas obrigatórias; suporta citação e marca de encaminhada.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Enviar localização",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestSendContactAndLocation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendLocationRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/send/menu": {
      "post": {
        "operationId": "sendMenu",
        "tags": [
          "Mensagens"
        ],
        "summary": "Enviar botões, lista ou enquete",
        "description": "renderMode=auto tenta nativo e pode cair para enquete/texto. rendered informa o formato entregue. 3 botões; 4–10 respostas podem virar lista; até 10 linhas; enquetes 2–12 opções. call: e carousel recusados.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Enviar botões, lista ou enquete",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendMenuResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestSendMenuParsesLegacyChoices"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMenuRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/message/forward": {
      "post": {
        "operationId": "forwardMessage",
        "tags": [
          "Mensagens"
        ],
        "summary": "Encaminhar mensagem guardada",
        "description": "Rota própria do AtendroZAP. Faz novo envio; mídias são baixadas e reenviadas, com marca de encaminhada.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Encaminhar mensagem guardada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "message_not_found ou media_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageActionRoutes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForwardRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/message/react": {
      "post": {
        "operationId": "reactMessage",
        "tags": [
          "Mensagens"
        ],
        "summary": "Reagir ou remover reação",
        "description": "Chat vem do ID guardado; number é ignorado. text vazio remove a reação.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Reagir ou remover reação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReactResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "message_not_found ou media_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageActionRoutes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReactRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/message/delete": {
      "post": {
        "operationId": "deleteMessage",
        "tags": [
          "Mensagens"
        ],
        "summary": "Apagar mensagem própria para todos",
        "description": "Mensagem recebida retorna cannot_delete. recorded=false indica WhatsApp aceitou mas o banco não gravou. Repetida pode retornar alreadyDeleted.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Apagar mensagem própria para todos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteMessageResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "message_not_found ou media_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageActionRoutes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteMessageRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/message/edit": {
      "post": {
        "operationId": "editMessage",
        "tags": [
          "Mensagens"
        ],
        "summary": "Editar texto ou legenda própria",
        "description": "Janela de 20 min. id/messageid do resultado identificam o original; editId identifica a ação. recorded=false exige reconciliação.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Editar texto ou legenda própria",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EditResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "message_not_found ou media_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageActionRoutes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      },
      "put": {
        "operationId": "editMessagePut",
        "tags": [
          "Mensagens"
        ],
        "summary": "Editar mensagem (alias PUT)",
        "description": "Mesmo contrato de POST /message/edit. Trocar POST por PUT com a mesma Idempotency-Key muda o hash da requisição.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Editar mensagem (alias PUT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EditResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Entrada inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large ou mídia acima do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "too_many_requests ou retry_later: nada enviado, respeitar Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "whatsapp_reachout_timelock / WHATSAPP_REACHOUT_TIMELOCK, provider_code=463.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "send_ambiguous ou action_ambiguous com ID. Reconciliar antes de repetir.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageActionRoutes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/message/find": {
      "post": {
        "operationId": "findMessages",
        "tags": [
          "Consultas"
        ],
        "summary": "Buscar mensagens no formato legado",
        "description": "Ordem mais recente primeiro. includeHistory=true por padrão; histórico nunca dispara automações novas. id ausente no store retorna messages=[].",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Buscar mensagens no formato legado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindMessagesResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "invalid_limit ou invalid_chatid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestFindMessagesLegacyShape",
          "TestPhoneChatFiltersResolveThroughTheSession"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FindMessagesRequest"
              }
            }
          }
        }
      }
    },
    "/message/status/{id}": {
      "get": {
        "operationId": "getMessageStatus",
        "tags": [
          "Consultas"
        ],
        "summary": "Consultar resultado da mensagem",
        "description": "Estados internos em minúsculas; usar para reconciliar resultado ambíguo. Preferir ID bruto messageid devolvido no envio.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar resultado da mensagem",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageStatus"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "description": "message_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageListingStatusAndEvents"
        ]
      }
    },
    "/v1/messages": {
      "get": {
        "operationId": "listStoredMessages",
        "tags": [
          "Consultas"
        ],
        "summary": "Listar mensagens no formato interno",
        "description": "Até 200, sem offset. Para paginação e formato legado use /message/find.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "chatid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Filtro exato do store; preferir JID."
            }
          },
          {
            "name": "excludeHistory",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Listar mensagens no formato interno",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListMessagesResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageListingStatusAndEvents"
        ]
      }
    },
    "/message/download": {
      "post": {
        "operationId": "downloadMessageMedia",
        "tags": [
          "Mídia"
        ],
        "summary": "Obter link assinado de mídia",
        "description": "Pode buscar mídia no WhatsApp e pedir reenvio ao celular; não é uma consulta sem efeitos. Link vence com retenção de 48 h, use expiresAt.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Obter link assinado de mídia",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DownloadResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "invalid_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "message_not_found ou media_unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "media_reupload_pending; aguardar reenvio ao celular.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "media_too_large; limite 64 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "not_media.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestDownloadAndServeMedia"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DownloadRequest"
              }
            }
          }
        }
      }
    },
    "/v1/media/{token}": {
      "get": {
        "operationId": "getSignedMedia",
        "tags": [
          "Mídia"
        ],
        "summary": "Ler bytes pelo link assinado",
        "description": "Sem header token: a credencial é o token na URL assinada devolvida pela API. Não registrar/compartilhar o link. Expirado ou inválido retorna 404. Não aceita upload.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "description": "Token assinado do fileURL; usar a URL inteira devolvida pelo download."
          }
        ],
        "responses": {
          "200": {
            "description": "Bytes da mídia; Content-Length presente. Tipos passivos inline; conteúdo ativo vira attachment/application/octet-stream com CSP sandbox.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "description": "Link inválido, expirado ou mídia não encontrada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestDownloadAndServeMedia"
        ]
      }
    },
    "/message/markread": {
      "post": {
        "operationId": "markMessagesRead",
        "tags": [
          "Leitura"
        ],
        "summary": "Enviar recibos de leitura por ID",
        "description": "Até 1000 IDs; mensagens próprias e desconhecidas são ignoradas. Afeta leitura no WhatsApp.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Enviar recibos de leitura por ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkReadResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestReadCheckAndDetails"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarkReadRequest"
              }
            }
          }
        }
      }
    },
    "/chat/read": {
      "post": {
        "operationId": "markChatRead",
        "tags": [
          "Leitura"
        ],
        "summary": "Marcar recebidas do chat como lidas",
        "description": "Envia recibo para até 100 mensagens ainda não lidas. read=false retorna marked=0 e note; não há suporte a marcar como não lido.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Marcar recebidas do chat como lidas",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatReadResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestReadCheckAndDetails"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatReadRequest"
              }
            }
          }
        }
      }
    },
    "/chat/check": {
      "post": {
        "operationId": "checkNumbers",
        "tags": [
          "Consultas"
        ],
        "summary": "Verificar números no WhatsApp",
        "description": "1–50 telefones; number é alias singular de numbers. Exige sessão conectada.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Verificar números no WhatsApp",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NumberCheck"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestReadCheckAndDetails"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckRequest"
              }
            }
          }
        }
      }
    },
    "/chat/details": {
      "post": {
        "operationId": "getChatDetails",
        "tags": [
          "Consultas"
        ],
        "summary": "Obter identidade e foto",
        "description": "Foto em cache por 24 h; campos vazios dependem de privacidade do WhatsApp. Não devolver dados de terceiros em logs.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Obter identidade e foto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatDetails"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "description": "number_not_on_whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestReadCheckAndDetails"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DetailsRequest"
              }
            }
          }
        }
      }
    },
    "/chat/find": {
      "post": {
        "operationId": "findChats",
        "tags": [
          "Consultas"
        ],
        "summary": "Listar conversas conhecidas",
        "description": "Não consulta histórico remoto sob demanda. wa_lastMsgTimestamp e wa_lastMessageTime estão em segundos. Sem filtros de lead/etiqueta.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Listar conversas conhecidas",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindChatsResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestChatFindAndAdminRoutes"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FindChatsRequest"
              }
            }
          }
        }
      }
    },
    "/group/list": {
      "get": {
        "operationId": "listGroups",
        "tags": [
          "Grupos"
        ],
        "summary": "Listar grupos sincronizados",
        "description": "Contrato implementado; homologação real de grupos pendente. force=true consulta WhatsApp; noparticipants=true omite Participants.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "noparticipants",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Listar grupos sincronizados",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Group"
                      }
                    }
                  },
                  "required": [
                    "groups"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Grupo, participantes, nome ou imagem inválidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "not_in_group ou not_admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "group_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Sessão desconectada/ocupada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited, Retry-After=30.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGroupRoutesFollowTheLegacyContract"
        ]
      }
    },
    "/group/info": {
      "post": {
        "operationId": "getGroupInfo",
        "tags": [
          "Grupos"
        ],
        "summary": "Consultar grupo",
        "description": "Cache de 10 min; force=true atualiza. getInviteLink só retorna link para administrador. getRequestsParticipants ignorado.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar grupo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Grupo, participantes, nome ou imagem inválidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "not_in_group ou not_admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "group_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Sessão desconectada/ocupada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited, Retry-After=30.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGroupRoutesFollowTheLegacyContract"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupInfoRequest"
              }
            }
          }
        }
      }
    },
    "/group/create": {
      "post": {
        "operationId": "createGroup",
        "tags": [
          "Grupos"
        ],
        "summary": "Criar grupo",
        "description": "Nome até 100 caracteres e até 50 participantes na criação. Conferir Error por participante. Exige autorização para grupo de teste.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Criar grupo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Group"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Grupo, participantes, nome ou imagem inválidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "not_in_group ou not_admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "group_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited, Retry-After=30.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGroupRoutesFollowTheLegacyContract"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateGroupRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/group/updateParticipants": {
      "post": {
        "operationId": "updateGroupParticipants",
        "tags": [
          "Grupos"
        ],
        "summary": "Alterar participantes",
        "description": "add/remove/promote/demote. approve/reject retornam 400 invalid_action. Conferir Error por participante e atualizar o grupo.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Alterar participantes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupParticipantsResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Grupo, participantes, nome ou imagem inválidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "not_in_group ou not_admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "group_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited, Retry-After=30.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGroupRoutesFollowTheLegacyContract"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupParticipantsRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/group/updateName": {
      "post": {
        "operationId": "updateGroupName",
        "tags": [
          "Grupos"
        ],
        "summary": "Alterar nome do grupo",
        "description": "Nome até 100 caracteres. Efeito real em grupo; homologação de laboratório pendente.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Alterar nome do grupo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupUpdateResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Grupo, participantes, nome ou imagem inválidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "not_in_group ou not_admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "group_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited, Retry-After=30.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGroupRoutesFollowTheLegacyContract"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupNameRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/group/updateImage": {
      "post": {
        "operationId": "updateGroupImage",
        "tags": [
          "Grupos"
        ],
        "summary": "Alterar imagem do grupo",
        "description": "JPEG/PNG/GIF até 8 MiB; corpo JSON até 12 MiB; convertido em JPEG até 640×640. remove/delete removem a foto.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Alterar imagem do grupo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupUpdateResult"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true quando a resposta guardada foi repetida.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "Grupo, participantes, nome ou imagem inválidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "not_in_group ou not_admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "group_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflito de sessão ou idempotency_in_progress; respeitar o código error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited, Retry-After=30.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "image_too_large ou body_too_large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "invalid_image.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Falha semântica da operação ou idempotency_mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGroupRoutesFollowTheLegacyContract"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupImageRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/instance/proxy": {
      "get": {
        "operationId": "getInstanceProxy",
        "tags": [
          "Proxy"
        ],
        "summary": "Consultar proxy da instância",
        "description": "Credenciais da URL são redigidas; não há pool gerenciado nem fallback automático.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar proxy da instância",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Proxy"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestInstanceProxyRoutes"
        ]
      },
      "post": {
        "operationId": "setInstanceProxy",
        "tags": [
          "Proxy"
        ],
        "summary": "Configurar proxy ou conexão direta",
        "description": "custom sonda conectividade antes de gravar. internal significa conexão direta; none exige confirm_no_proxy=true. Mudança pode reiniciar sessão; não usar para testar cadastro.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Configurar proxy ou conexão direta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProxyResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "proxy_unreachable, invalid_proxy_url, invalid_mode ou confirm_no_proxy_required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "proxy_required ou secret_unreadable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestInstanceProxyRoutes",
          "TestProxyRequiredWorkerRefusesDirectMode"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProxyRequest"
              }
            }
          }
        }
      }
    },
    "/webhook": {
      "post": {
        "operationId": "configureWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Configurar, desativar ou herdar webhook",
        "description": "Um destino efetivo por instância. Cadastro cria exceção ao global. URL nova começa após o backlog; mesma URL preserva cursor e segredo omitido. action=delete desativa inclusive em mudanças globais; action=inherit volta ao global. groups e call são entregues; qrcode continua ignorado. call é emitido pelas instâncias calls.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Cadastrar, atualizar ou remover webhook",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWriteResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "invalid_url/event/events/exclude/secret, invalid_action ou unsupported_option.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "webhooks_disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestWebhookRegistrationAndEvents",
          "TestCallWebhookSubscription"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookRequest"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Consultar webhook e entrega",
        "description": "Sem cadastro: registered=false, webhooks=[]. Com cadastro inclui webhooks=[registro] e deliveryStatus. Nunca revela o secret.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar webhook e entrega",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestWebhookRegistrationAndEvents"
        ]
      }
    },
    "/webhook/errors": {
      "get": {
        "operationId": "getWebhookErrors",
        "tags": [
          "Webhooks"
        ],
        "summary": "Consultar falhas recentes de entrega",
        "description": "Falhas deste worker, sem payload; também inclui estado persistido quando houver webhook.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar falhas recentes de entrega",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookErrors"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestWebhookRegistrationAndEvents"
        ]
      }
    },
    "/v1/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Webhooks"
        ],
        "summary": "Inspecionar eventos internos e fila",
        "description": "Formato persistido difere do envelope legado entregue. Consultar EVENTS.md. Não expor payloads em logs.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivered",
                "dead"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Inspecionar eventos internos e fila",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventsResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestMessageListingStatusAndEvents",
          "TestWebhookRegistrationAndEvents"
        ]
      }
    },
    "/v1/events/{id}/replay": {
      "post": {
        "operationId": "replayEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "Reenfileirar evento",
        "description": "Reinicia orçamento de tentativas; pode repetir automações no consumidor. Deduplicar pelo ID do evento. Exige avaliação antes do replay real.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Reenfileirar evento",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReplayResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "description": "event_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "event_before_registration; anterior ao webhook atual ou sem webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestWebhookRegistrationAndEvents"
        ]
      }
    },
    "/call/make": {
      "post": {
        "operationId": "makeCall",
        "tags": [
          "Ligações"
        ],
        "summary": "Iniciar ligação por instância calls",
        "description": "Exige kind=calls conectado e módulo ligado. Retorna 201 ao iniciar; acompanhar o estado. Em instância whatsapp mantém 501 calls_not_supported. Não equivale à chamada sem áudio do provedor legado.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "line_busy, not_connected, owned_elsewhere ou idempotency_in_progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported: módulo desligado; /call/make também recusa instância whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "201": {
            "description": "Chamada iniciada; acompanhar pelo callId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "true ao repetir a resposta guardada.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "invalid_number ou invalid_json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "number_not_on_whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "idempotency_mismatch: chave repetida com corpo diferente.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "call_failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "retry_later antes de iniciar a chamada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCallRoutesOnACallsInstance",
          "TestCallRoutesAreGatedByKind",
          "TestCallRoutesMapSessionErrors"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MakeCallRequest"
              }
            }
          }
        },
        "x-idempotency-retention-seconds": 86400
      }
    },
    "/v1/webhook": {
      "post": {
        "operationId": "configureGlobalWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Salvar ou remover webhook global",
        "description": "Configuração persistida por servidor. Instâncias sem exceção herdam automaticamente. Atualização transacional preserva configurações próprias. action=delete limpa o global; a configuração salva prevalece sobre bootstrap por ambiente. Segredo nunca é devolvido.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Salvar ou remover webhook global",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GlobalWebhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "invalid_url/event/events/exclude/secret, invalid_action ou unsupported_option.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "webhooks_disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGlobalAndAdminWebhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GlobalWebhookRequest"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getGlobalWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Consultar webhook global",
        "description": "Configuração persistida por servidor. Instâncias sem exceção herdam automaticamente. Atualização transacional preserva configurações próprias. action=delete limpa o global; a configuração salva prevalece sobre bootstrap por ambiente. Segredo nunca é devolvido.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar webhook global",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GlobalWebhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGlobalAndAdminWebhooks"
        ]
      }
    },
    "/v1/instances/{id}/webhook": {
      "post": {
        "operationId": "adminConfigureWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Configurar, desativar ou herdar webhook por ID",
        "description": "Um destino efetivo por instância. Cadastro cria exceção ao global. URL nova começa após o backlog; mesma URL preserva cursor e segredo omitido. action=delete desativa inclusive em mudanças globais; action=inherit volta ao global. groups e call são entregues; qrcode continua ignorado. call é emitido pelas instâncias calls.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Cadastrar, atualizar ou remover webhook",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWriteResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "400": {
            "description": "invalid_url/event/events/exclude/secret, invalid_action ou unsupported_option.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "webhooks_disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGlobalAndAdminWebhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookRequest"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "adminGetWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Consultar webhook e entrega por ID",
        "description": "Sem cadastro: registered=false, webhooks=[]. Com cadastro inclui webhooks=[registro] e deliveryStatus. Nunca revela o secret.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar webhook e entrega",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGlobalAndAdminWebhooks"
        ]
      }
    },
    "/v1/instances/{id}/webhook/errors": {
      "get": {
        "operationId": "adminGetWebhookErrors",
        "tags": [
          "Webhooks"
        ],
        "summary": "Consultar falhas recentes de entrega por ID",
        "description": "Falhas deste worker, sem payload; também inclui estado persistido quando houver webhook.",
        "security": [
          {
            "AdminToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "format": "uuid"
            },
            "description": "Identificador devolvido pela API."
          }
        ],
        "responses": {
          "200": {
            "description": "Consultar falhas recentes de entrega",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookErrors"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/StorageUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "description": "instance_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestGlobalAndAdminWebhooks"
        ]
      }
    },
    "/call/answer": {
      "post": {
        "operationId": "answerCall",
        "tags": [
          "Ligações"
        ],
        "summary": "Atender ligação recebida",
        "description": "Token da instância calls que possui a chamada. Corpo com callId; a operação renova o lease antes do efeito externo.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "wrong_instance_kind, not_connected, owned_elsewhere ou invalid_call_state; conferir error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported: módulo desligado; /call/make também recusa instância whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "Estado da chamada após a ação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "400": {
            "description": "invalid_json ou invalid_call_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "call_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "retry_later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCallRoutesOnACallsInstance",
          "TestCallRoutesAreGatedByKind",
          "TestCallRoutesMapSessionErrors"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallActionRequest"
              }
            }
          }
        }
      }
    },
    "/call/reject": {
      "post": {
        "operationId": "rejectCall",
        "tags": [
          "Ligações"
        ],
        "summary": "Recusar ligação recebida",
        "description": "Token da instância calls que possui a chamada. Corpo com callId; a operação renova o lease antes do efeito externo.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "wrong_instance_kind, not_connected, owned_elsewhere ou invalid_call_state; conferir error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported: módulo desligado; /call/make também recusa instância whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "Estado da chamada após a ação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "400": {
            "description": "invalid_json ou invalid_call_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "call_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "retry_later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCallRoutesOnACallsInstance",
          "TestCallRoutesAreGatedByKind",
          "TestCallRoutesMapSessionErrors"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallActionRequest"
              }
            }
          }
        }
      }
    },
    "/call/hangup": {
      "post": {
        "operationId": "hangupCall",
        "tags": [
          "Ligações"
        ],
        "summary": "Encerrar ligação",
        "description": "Token da instância calls que possui a chamada. Corpo com callId; a operação renova o lease antes do efeito externo.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "wrong_instance_kind, not_connected, owned_elsewhere ou invalid_call_state; conferir error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported: módulo desligado; /call/make também recusa instância whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "Estado da chamada após a ação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "400": {
            "description": "invalid_json ou invalid_call_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "call_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "retry_later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCallRoutesOnACallsInstance",
          "TestCallRoutesAreGatedByKind",
          "TestCallRoutesMapSessionErrors"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallActionRequest"
              }
            }
          }
        }
      }
    },
    "/call/active": {
      "get": {
        "operationId": "listActiveCalls",
        "tags": [
          "Ligações"
        ],
        "summary": "Listar ligações ativas",
        "description": "Lista as chamadas vivas da instância calls neste worker.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "wrong_instance_kind, not_connected, owned_elsewhere ou invalid_call_state; conferir error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported: módulo desligado; /call/make também recusa instância whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "Chamadas vivas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActiveCalls"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCallRoutesOnACallsInstance",
          "TestCallRoutesAreGatedByKind"
        ]
      }
    },
    "/call/{id}": {
      "get": {
        "operationId": "getCall",
        "tags": [
          "Ligações"
        ],
        "summary": "Consultar estado de ligação",
        "description": "Consulta chamada viva ou recém-encerrada mantida na memória deste worker.",
        "security": [
          {
            "InstanceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "description": "callId retornado pela API."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "wrong_instance_kind, not_connected, owned_elsewhere ou invalid_call_state; conferir error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported: módulo desligado; /call/make também recusa instância whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "Estado da chamada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "404": {
            "description": "call_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCallRoutesOnACallsInstance"
        ]
      }
    },
    "/call/{id}/audio": {
      "get": {
        "operationId": "streamCallAudio",
        "tags": [
          "Ligações"
        ],
        "summary": "Abrir ponte WebSocket de áudio",
        "description": "Upgrade WebSocket. PCM s16le mono 16 kHz binário nos dois sentidos; origem validada por ATENDROZAP_CALLS_AUDIO_ORIGINS. Aceita token no header ou na query somente nesta rota. O console faz proxy com o header e guarda o token fora do navegador. Fechar o socket encerra a chamada; um segundo socket substitui o primeiro. Códigos 4000 encerrada, 4001 PCM ímpar, 4002 falha de escrita, 4003 substituído.",
        "security": [
          {
            "InstanceToken": []
          },
          {
            "InstanceQueryToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "description": "callId retornado pela API."
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "wrong_instance_kind, not_connected, owned_elsewhere ou invalid_call_state; conferir error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "calls_not_supported: módulo desligado; /call/make também recusa instância whatsapp.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "101": {
            "description": "WebSocket estabelecido; mensagens binárias PCM."
          },
          "400": {
            "description": "Upgrade WebSocket inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Origem de navegador não autorizada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "call_not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implemented": true,
        "x-contract-tests": [
          "TestCallAudioAcceptsTheTokenInTheQuery",
          "TestCallRoutesAreGatedByKind"
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "AdminToken": {
        "type": "apiKey",
        "in": "header",
        "name": "admintoken",
        "description": "ATENDROZAP_ADMIN_TOKEN do servidor. Somente backend administrativo; nunca frontend/documentação."
      },
      "InstanceToken": {
        "type": "apiKey",
        "in": "header",
        "name": "token",
        "description": "instance.token emitido no init/reemissão. Não é o admin token. instancekey/Authorization/query não substituem este header."
      },
      "InstanceQueryToken": {
        "type": "apiKey",
        "in": "query",
        "name": "token",
        "description": "Alternativa exclusiva da ponte WebSocket. Preferir o proxy do console, que mantém a credencial no backend. Não registrar a query."
      }
    },
    "parameters": {
      "RequestId": {
        "name": "X-Request-Id",
        "in": "header",
        "required": false,
        "description": "ASCII visível sem espaços, 1–128 caracteres. Inválido/ausente é substituído por UUID. Ecoado na resposta.",
        "schema": {
          "type": "string",
          "maxLength": 128
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "ASCII visível sem espaços, 1–128. Repetir método, caminho e os mesmos bytes do JSON. 409 em andamento; 422 se mudar a requisição. 504 é guardado e não deve causar reenvio automático.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Falha; usar HTTP status e error. 404 rota inexistente; 405 método inválido com Allow. Ver catálogo no guia de integração.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "unauthorized: header ausente, inválido ou duplicado. Bearer/query/instancekey não substituem admintoken/token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "StorageUnavailable": {
        "description": "storage_unavailable (Retry-After=5). Algumas rotas também retornam 502 por falha da conexão/mídia; consultar error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "string"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "messageid": {
            "type": "string",
            "description": "ID para reconciliação quando o resultado é ambíguo."
          },
          "messageId": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "provider_code": {
            "type": "integer"
          },
          "error_key": {
            "type": "string"
          },
          "error_source": {
            "type": "string"
          },
          "details": {
            "type": "object"
          },
          "instance": {
            "$ref": "#/components/schemas/Instance"
          }
        },
        "required": [
          "error"
        ]
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "alive",
              "ready",
              "not_ready"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "database_unreachable"
            ]
          }
        },
        "required": [
          "status"
        ]
      },
      "Capabilities": {
        "type": "object",
        "properties": {
          "service": {
            "type": "string",
            "enum": [
              "AtendroZAP"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "laboratory"
            ]
          },
          "worker": {
            "type": "string"
          },
          "engine": {
            "type": "object"
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "calls": {
                "type": "boolean",
                "description": "Habilitado neste worker por ATENDROZAP_CALLS_ENABLED; exige instância calls."
              },
              "ptt_transcoding": {
                "type": "boolean",
                "enum": [
                  false
                ]
              }
            },
            "additionalProperties": {
              "type": "boolean"
            }
          },
          "interactive": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "webhookEvents": {
            "type": "object",
            "properties": {
              "delivered": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "ignored": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "instance_kinds": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "whatsapp",
                "calls"
              ]
            }
          },
          "calls_max_concurrent": {
            "type": "integer",
            "minimum": 0,
            "description": "Teto por instância; padrão 1 quando configurado."
          }
        },
        "required": [
          "service",
          "status",
          "capabilities"
        ]
      },
      "Instance": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "systemName": {
            "type": "string"
          },
          "companyId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "disconnected",
              "connecting",
              "connected"
            ]
          },
          "owner": {
            "type": "string"
          },
          "profileName": {
            "type": "string"
          },
          "qrcode": {
            "type": "string",
            "description": "PNG em data URI quando há QR vigente; vazio no restante e nas listagens."
          },
          "paircode": {
            "type": "string"
          },
          "lastDisconnect": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "lastDisconnectReason": {
            "type": "string"
          },
          "disconnectCode": {
            "type": "string"
          },
          "worker": {
            "type": "string"
          },
          "limitEnforcement": {
            "type": "string"
          },
          "limitUntil": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "created": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "enum": [
              "whatsapp",
              "calls"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "qrcode",
          "owner",
          "profileName",
          "kind"
        ]
      },
      "IssuedInstance": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Instance"
          },
          {
            "type": "object",
            "properties": {
              "token": {
                "type": "string",
                "description": "Segredo de instância: guardar no backend. Só criação/reemissão; replay idempotente de init pode repetir por 15 min.",
                "minLength": 64,
                "maxLength": 64
              }
            },
            "required": [
              "token"
            ]
          }
        ]
      },
      "Connection": {
        "type": "object",
        "properties": {
          "connected": {
            "type": "boolean",
            "description": "Socket conectado."
          },
          "loggedIn": {
            "type": "boolean",
            "description": "Credencial de sessão autenticada; usar junto de connected."
          },
          "jid": {
            "type": "string"
          },
          "instance": {
            "$ref": "#/components/schemas/Instance"
          },
          "webhooks": {
            "type": "object",
            "description": "Presente no status, inclui registered e a saúde de entrega quando registrada."
          }
        },
        "required": [
          "connected",
          "loggedIn",
          "jid",
          "instance"
        ]
      },
      "InitRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Obrigatório, até 128 bytes UTF-8, sem controle ou espaços nas extremidades.",
            "minLength": 1,
            "maxLength": 128
          },
          "systemName": {
            "type": "string",
            "description": "Até 128 bytes; recomendado Atendro.",
            "maxLength": 128
          },
          "companyId": {
            "type": "string",
            "description": "Até 128 bytes; vínculo com tenant no consumidor. Filtro de lista, não autorização.",
            "maxLength": 128
          },
          "kind": {
            "type": "string",
            "enum": [
              "whatsapp",
              "calls"
            ],
            "default": "whatsapp",
            "description": "Imutável após criar. calls exige o módulo habilitado e pareamento próprio."
          }
        },
        "required": [
          "name"
        ]
      },
      "InitResult": {
        "type": "object",
        "properties": {
          "response": {
            "type": "string"
          },
          "connected": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "loggedIn": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "instance": {
            "$ref": "#/components/schemas/IssuedInstance"
          }
        },
        "required": [
          "response",
          "connected",
          "loggedIn",
          "instance"
        ]
      },
      "ConnectRequest": {
        "type": "object",
        "properties": {
          "phone": {
            "type": "string",
            "description": "Opcional. Sem phone usa QR; com phone extrai 8–15 dígitos incluindo DDI e pede código de pareamento."
          }
        }
      },
      "DisconnectResult": {
        "type": "object",
        "properties": {
          "response": {
            "type": "string"
          },
          "unlinked": {
            "type": "boolean",
            "description": "Indica confirmação remota. Credencial local é limpa mesmo se false."
          },
          "instance": {
            "$ref": "#/components/schemas/Instance"
          }
        },
        "required": [
          "response",
          "unlinked",
          "instance"
        ]
      },
      "RestartResult": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Connection"
          },
          {
            "type": "object",
            "properties": {
              "response": {
                "type": "string"
              }
            },
            "required": [
              "response"
            ]
          }
        ]
      },
      "ResponseMessage": {
        "type": "object",
        "properties": {
          "response": {
            "type": "string"
          }
        },
        "required": [
          "response"
        ]
      },
      "TokenRequest": {
        "type": "object",
        "properties": {
          "graceSeconds": {
            "type": "integer",
            "description": "Prazo de convivência do token anterior; zero revoga imediatamente.",
            "minimum": 0,
            "maximum": 3600,
            "default": 0
          }
        }
      },
      "TokenResult": {
        "type": "object",
        "properties": {
          "response": {
            "type": "string"
          },
          "graceSeconds": {
            "type": "integer"
          },
          "instance": {
            "$ref": "#/components/schemas/IssuedInstance"
          }
        },
        "required": [
          "response",
          "graceSeconds",
          "instance"
        ]
      },
      "SendTextRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "replyid": {
            "type": "string",
            "description": "ID de mensagem guardada para citação; desconhecida retorna 422 reply_not_found."
          },
          "delay": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Espera de digitação em milissegundos; o manager limita a 15000 ms."
          },
          "forward": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "text": {
            "type": "string",
            "description": "UTF-8, não vazio/branco, até 65536 bytes.",
            "minLength": 1,
            "maxLength": 65536
          },
          "linkPreview": {
            "description": "Aceito e ignorado; o serviço não gera prévia de link."
          }
        },
        "required": [
          "number",
          "text"
        ]
      },
      "SendMediaRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "replyid": {
            "type": "string",
            "description": "ID de mensagem guardada para citação; desconhecida retorna 422 reply_not_found."
          },
          "delay": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Espera de digitação em milissegundos; o manager limita a 15000 ms."
          },
          "forward": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "ptt",
              "document",
              "sticker"
            ]
          },
          "file": {
            "type": "string",
            "description": "Data URI, base64 ou URL permitida por ATENDROZAP_MEDIA_URL_ALLOWLIST. Até 32 MiB decodificados; JSON até 48 MiB.",
            "minLength": 1
          },
          "text": {
            "type": "string",
            "description": "Legenda em UTF-8, até 65536 bytes.",
            "maxLength": 65536
          },
          "docName": {
            "type": "string",
            "description": "Nome-base do documento."
          }
        },
        "required": [
          "number",
          "type",
          "file"
        ]
      },
      "SendContactRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "replyid": {
            "type": "string",
            "description": "ID de mensagem guardada para citação; desconhecida retorna 422 reply_not_found."
          },
          "delay": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Espera de digitação em milissegundos; o manager limita a 15000 ms."
          },
          "forward": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "fullName": {
            "type": "string",
            "description": "Nome do contato sintético/autorizado, 1–128 bytes após trim.",
            "minLength": 1,
            "maxLength": 128
          },
          "phoneNumber": {
            "type": "string",
            "description": "Telefone do contato: 8–15 dígitos com DDI após retirar formatação."
          },
          "organization": {
            "type": "string",
            "description": "Até 128 bytes.",
            "maxLength": 128
          }
        },
        "required": [
          "number",
          "fullName",
          "phoneNumber"
        ]
      },
      "SendLocationRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "replyid": {
            "type": "string",
            "description": "ID de mensagem guardada para citação; desconhecida retorna 422 reply_not_found."
          },
          "delay": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Espera de digitação em milissegundos; o manager limita a 15000 ms."
          },
          "forward": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "latitude": {
            "type": "number",
            "minimum": -90,
            "maximum": 90
          },
          "longitude": {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          "name": {
            "type": "string",
            "description": "Até 256 bytes.",
            "maxLength": 256
          },
          "address": {
            "type": "string",
            "description": "Até 512 bytes.",
            "maxLength": 512
          }
        },
        "required": [
          "number",
          "latitude",
          "longitude"
        ]
      },
      "SendMenuRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "replyid": {
            "type": "string",
            "description": "ID de mensagem guardada para citação; desconhecida retorna 422 reply_not_found."
          },
          "delay": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Espera de digitação em milissegundos; o manager limita a 15000 ms."
          },
          "forward": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "button",
              "buttons",
              "list",
              "poll"
            ]
          },
          "text": {
            "type": "string",
            "description": "Até 4096 bytes.",
            "minLength": 1,
            "maxLength": 4096
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "label|id|descrição, label\nid ou label; [Seção] nas listas; url:/copy: em botões. call: não suportado."
            },
            "minItems": 1
          },
          "footerText": {
            "type": "string"
          },
          "listButton": {
            "type": "string"
          },
          "selectableCount": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Só em type=poll; padrão 1."
          },
          "renderMode": {
            "type": "string",
            "enum": [
              "auto",
              "native",
              "poll",
              "text"
            ],
            "default": "auto"
          }
        },
        "required": [
          "number",
          "type",
          "text",
          "choices"
        ]
      },
      "SendResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "messageid": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "timestamp": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          }
        },
        "required": [
          "success",
          "messageid",
          "messageId",
          "id",
          "timestamp"
        ]
      },
      "SendMediaResult": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SendResult"
          },
          {
            "type": "object",
            "properties": {
              "mimetype": {
                "type": "string"
              },
              "ptt": {
                "type": "boolean"
              },
              "note": {
                "type": "string",
                "description": "Explica envio como áudio comum quando PTT não é possível."
              }
            },
            "required": [
              "mimetype",
              "ptt"
            ]
          }
        ]
      },
      "SendMenuResult": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SendResult"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "renderMode": {
                "type": "string",
                "enum": [
                  "auto",
                  "native",
                  "poll",
                  "text"
                ]
              },
              "rendered": {
                "type": "string",
                "enum": [
                  "button",
                  "list",
                  "poll",
                  "text"
                ]
              }
            },
            "required": [
              "type",
              "renderMode",
              "rendered"
            ]
          }
        ]
      },
      "ReactRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "messageId": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "number": {
            "type": "string",
            "description": "Ignorado para reação, edição e exclusão; o chat vem da mensagem guardada."
          },
          "text": {
            "type": "string",
            "description": "Um emoji; vazio/ausente remove a reação."
          }
        },
        "anyOf": [
          {
            "required": [
              "id"
            ]
          },
          {
            "required": [
              "messageId"
            ]
          }
        ]
      },
      "DeleteMessageRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "messageId": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "number": {
            "type": "string",
            "description": "Ignorado para reação, edição e exclusão; o chat vem da mensagem guardada."
          }
        },
        "anyOf": [
          {
            "required": [
              "id"
            ]
          },
          {
            "required": [
              "messageId"
            ]
          }
        ]
      },
      "EditRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "messageId": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "number": {
            "type": "string",
            "description": "Ignorado para reação, edição e exclusão; o chat vem da mensagem guardada."
          },
          "text": {
            "type": "string",
            "description": "Texto/legenda UTF-8, não vazio, até 65536 bytes.",
            "minLength": 1,
            "maxLength": 65536
          }
        },
        "required": [
          "text"
        ],
        "anyOf": [
          {
            "required": [
              "id"
            ]
          },
          {
            "required": [
              "messageId"
            ]
          }
        ]
      },
      "ForwardRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "messageId": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "delay": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Espera de digitação em milissegundos; o manager limita a 15000 ms."
          }
        },
        "required": [
          "number"
        ],
        "anyOf": [
          {
            "required": [
              "id"
            ]
          },
          {
            "required": [
              "messageId"
            ]
          }
        ]
      },
      "ReactResult": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SendResult"
          },
          {
            "type": "object",
            "properties": {
              "reaction": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "emoji": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "sent",
                      "removed"
                    ]
                  }
                },
                "required": [
                  "id",
                  "emoji",
                  "status"
                ]
              }
            },
            "required": [
              "reaction"
            ]
          }
        ]
      },
      "DeleteMessageResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "id": {
            "type": "string"
          },
          "messageid": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "Deleted"
            ]
          },
          "timestamp": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "recorded": {
            "type": "boolean"
          },
          "alreadyDeleted": {
            "type": "boolean"
          }
        },
        "required": [
          "success",
          "id",
          "status",
          "timestamp",
          "recorded"
        ]
      },
      "EditResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "id": {
            "type": "string",
            "description": "owner:id do original."
          },
          "messageid": {
            "type": "string",
            "description": "ID do original."
          },
          "messageId": {
            "type": "string"
          },
          "editId": {
            "type": "string",
            "description": "ID da ação de edição."
          },
          "content": {
            "type": "string"
          },
          "messageType": {
            "type": "string"
          },
          "messageTimestamp": {
            "type": "integer",
            "description": "Unix em milissegundos.",
            "format": "int64"
          },
          "timestamp": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          },
          "status": {
            "type": "string"
          },
          "owner": {
            "type": "string"
          },
          "editCount": {
            "type": "integer"
          },
          "recorded": {
            "type": "boolean",
            "description": "false: WhatsApp aceitou mas a persistência local falhou."
          }
        },
        "required": [
          "success",
          "id",
          "messageid",
          "editId",
          "recorded"
        ]
      },
      "FindMessagesRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          },
          "chatid": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "limit": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 200
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "0/ausente usa 100; máximo 200.",
            "default": 100
          },
          "offset": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Deslocamento da página."
          },
          "includeHistory": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ],
            "default": true
          }
        }
      },
      "LegacyMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "owner:messageid."
          },
          "messageid": {
            "type": "string"
          },
          "chatid": {
            "type": "string"
          },
          "sender": {
            "type": "string"
          },
          "sender_pn": {
            "type": "string"
          },
          "senderName": {
            "type": "string"
          },
          "pushName": {
            "type": "string"
          },
          "isGroup": {
            "type": "boolean"
          },
          "fromMe": {
            "type": "boolean"
          },
          "wasSentByApi": {
            "type": "boolean"
          },
          "messageType": {
            "type": "string",
            "description": "Tipo legado PascalCase."
          },
          "type": {
            "type": "string"
          },
          "messageTimestamp": {
            "type": "integer",
            "description": "Unix em milissegundos.",
            "format": "int64"
          },
          "timestamp": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          },
          "status": {
            "type": "string",
            "description": "Pending, Sent, Delivered, Read, Played, Failed, Received ou Deleted."
          },
          "text": {
            "type": "string"
          },
          "content": {
            "type": "object"
          },
          "buttonOrListid": {
            "type": "string"
          },
          "reactions": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "choices": {
            "description": "Opções armazenadas de menus/enquetes; estrutura depende do tipo e pode ser null."
          },
          "fileURL": {
            "type": "string"
          },
          "mediaUrl": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "messageid",
          "chatid",
          "fromMe"
        ]
      },
      "FindMessagesResult": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LegacyMessage"
            }
          },
          "returnedMessages": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextOffset": {
            "type": "integer"
          }
        },
        "required": [
          "messages",
          "returnedMessages",
          "limit",
          "offset",
          "hasMore"
        ]
      },
      "MessageStatus": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Estado interno, em minúsculas: sending, unknown, failed, sent, delivered, read, played ou received."
          },
          "failure": {
            "type": "string"
          },
          "fromMe": {
            "type": "boolean"
          },
          "timestamp": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          },
          "deliveredAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "readAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "playedAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "revokedAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "deletedForMeAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "editedAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "editCount": {
            "type": "integer"
          },
          "reactions": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object"
            }
          }
        },
        "required": [
          "id",
          "status",
          "failure",
          "fromMe",
          "timestamp"
        ]
      },
      "StoredMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "chatid": {
            "type": "string"
          },
          "fromMe": {
            "type": "boolean"
          },
          "status": {
            "type": "string"
          },
          "timestamp": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          }
        },
        "description": "Projeção interna session.DescribeMessage; campos de conteúdo variam com o tipo. Para o formato legado use /message/find."
      },
      "ListMessagesResult": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StoredMessage"
            }
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "required": [
          "messages",
          "hasMore"
        ]
      },
      "DownloadRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
            "minLength": 1
          }
        },
        "required": [
          "id"
        ]
      },
      "DownloadResult": {
        "type": "object",
        "properties": {
          "fileURL": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "mimetype": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "fileName": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "expiresAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "messageid": {
            "type": "string"
          }
        },
        "required": [
          "fileURL",
          "url",
          "mimetype",
          "mimeType",
          "fileName",
          "size",
          "expiresAt",
          "messageid"
        ]
      },
      "MarkReadRequest": {
        "type": "object",
        "properties": {
          "id": {
            "oneOf": [
              {
                "type": "string",
                "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
                "minLength": 1
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "ID da mensagem guardada nesta instância. Nos corpos aceita também owner:id.",
                  "minLength": 1
                },
                "minItems": 1,
                "maxItems": 1000
              }
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "MarkReadResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "marked": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "count": {
            "type": "integer"
          }
        },
        "required": [
          "success",
          "marked",
          "count"
        ]
      },
      "ChatReadRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          },
          "read": {
            "type": "boolean",
            "description": "Ausente equivale a true; false não marca como não lido e retorna marked=0 com note.",
            "default": true
          }
        },
        "required": [
          "number"
        ]
      },
      "ChatReadResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "marked": {
            "type": "integer"
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "success",
          "marked"
        ]
      },
      "CheckRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Um telefone com DDI."
          },
          "numbers": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "maxItems": 50
              }
            ]
          }
        },
        "anyOf": [
          {
            "required": [
              "number"
            ]
          },
          {
            "required": [
              "numbers"
            ]
          }
        ]
      },
      "NumberCheck": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "exists": {
            "type": "boolean"
          },
          "numberExists": {
            "type": "boolean"
          },
          "jid": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "properties": {
              "exists": {
                "type": "boolean"
              },
              "jid": {
                "type": "string"
              }
            }
          }
        },
        "required": [
          "query",
          "exists",
          "numberExists",
          "jid",
          "data"
        ]
      },
      "DetailsRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
            "minLength": 1
          }
        },
        "required": [
          "number"
        ]
      },
      "ChatDetails": {
        "type": "object",
        "properties": {
          "jid": {
            "type": "string"
          },
          "exists": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "pushName": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "verifiedName": {
            "type": "string"
          },
          "isBusiness": {
            "type": "boolean"
          },
          "profilePicUrl": {
            "type": "string"
          },
          "profilePictureUrl": {
            "type": "string"
          },
          "imagePreview": {
            "type": "string"
          },
          "pictureId": {
            "type": "string"
          },
          "imageState": {
            "type": "string"
          }
        },
        "required": [
          "jid",
          "exists",
          "profilePicUrl"
        ]
      },
      "FindChatsRequest": {
        "type": "object",
        "properties": {
          "limit": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 5000
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "0/ausente retorna até 5000.",
            "default": 5000
          },
          "offset": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "description": "Inteiro em formato decimal.",
                "pattern": "^\\d+$"
              }
            ],
            "description": "Deslocamento da página."
          },
          "wa_isGroup": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "includeLeft": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Chat": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "wa_chatid": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "wa_name": {
            "type": "string"
          },
          "wa_contactName": {
            "type": "string"
          },
          "wa_isGroup": {
            "type": "boolean"
          },
          "wa_unreadCount": {
            "type": "integer"
          },
          "wa_archived": {
            "type": "boolean"
          },
          "wa_lastMsgTimestamp": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          },
          "wa_lastMessageTime": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          },
          "owner": {
            "type": "string"
          },
          "phone": {
            "type": "string",
            "description": "Vazio para grupo ou LID sem telefone conhecido."
          },
          "pictureId": {
            "type": "string"
          },
          "wa_isGroup_announce": {
            "type": "boolean"
          },
          "participantCount": {
            "type": "integer"
          },
          "topic": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "wa_chatid",
          "wa_isGroup",
          "wa_lastMsgTimestamp",
          "wa_lastMessageTime"
        ]
      },
      "FindChatsResult": {
        "type": "object",
        "properties": {
          "chats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Chat"
            }
          },
          "hasMore": {
            "type": "boolean"
          },
          "pagination": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "integer"
              },
              "offset": {
                "type": "integer"
              },
              "returned": {
                "type": "integer"
              }
            },
            "required": [
              "limit",
              "offset",
              "returned"
            ]
          },
          "nextOffset": {
            "type": "integer"
          }
        },
        "required": [
          "chats",
          "hasMore",
          "pagination"
        ]
      },
      "GroupParticipant": {
        "type": "object",
        "properties": {
          "JID": {
            "type": "string"
          },
          "PhoneNumber": {
            "type": "string"
          },
          "LID": {
            "type": "string"
          },
          "IsAdmin": {
            "type": "boolean"
          },
          "IsSuperAdmin": {
            "type": "boolean"
          },
          "DisplayName": {
            "type": "string"
          },
          "Error": {
            "type": "integer",
            "description": "Código por participante; 0 indica ausência de erro registrado."
          }
        },
        "required": [
          "JID",
          "IsAdmin",
          "IsSuperAdmin",
          "DisplayName",
          "Error"
        ]
      },
      "Group": {
        "type": "object",
        "properties": {
          "JID": {
            "type": "string"
          },
          "Name": {
            "type": "string"
          },
          "Topic": {
            "type": "string"
          },
          "OwnerJID": {
            "type": "string"
          },
          "OwnerPN": {
            "type": "string"
          },
          "IsLocked": {
            "type": "boolean"
          },
          "IsAnnounce": {
            "type": "boolean"
          },
          "AddressingMode": {
            "type": "string"
          },
          "ParticipantCount": {
            "type": "integer"
          },
          "Participants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GroupParticipant"
            }
          },
          "invite_link": {
            "type": "string",
            "description": "Vazio se não solicitado ou sem permissão de admin."
          },
          "PictureID": {
            "type": "string"
          },
          "lastMessageAt": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          },
          "syncedAt": {
            "type": "integer",
            "description": "Unix em segundos.",
            "format": "int64"
          },
          "left": {
            "type": "boolean"
          },
          "GroupCreated": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          }
        },
        "required": [
          "JID",
          "Name",
          "ParticipantCount",
          "invite_link"
        ]
      },
      "GroupInfoRequest": {
        "type": "object",
        "properties": {
          "groupjid": {
            "type": "string",
            "description": "Identificador do grupo terminado em @g.us.",
            "minLength": 1
          },
          "force": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "getInviteLink": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "getRequestsParticipants": {
            "description": "Aceito e ignorado."
          }
        },
        "required": [
          "groupjid"
        ]
      },
      "CreateGroupRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
              "minLength": 1
            },
            "maxItems": 50,
            "minItems": 1
          }
        },
        "required": [
          "name",
          "participants"
        ]
      },
      "GroupParticipantsRequest": {
        "type": "object",
        "properties": {
          "groupjid": {
            "type": "string",
            "description": "Identificador do grupo terminado em @g.us.",
            "minLength": 1
          },
          "action": {
            "type": "string",
            "enum": [
              "add",
              "remove",
              "promote",
              "demote"
            ]
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "Dígitos com DDI (8–15) ou identificador @s.whatsapp.net, @g.us ou @lid. Nunca inferir telefone de um LID.",
              "minLength": 1
            },
            "minItems": 1
          }
        },
        "required": [
          "groupjid",
          "action",
          "participants"
        ]
      },
      "GroupParticipantsResult": {
        "type": "object",
        "properties": {
          "groupUpdated": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "JID": {
                  "type": "string"
                },
                "Error": {
                  "type": "integer"
                }
              },
              "required": [
                "JID",
                "Error"
              ]
            }
          },
          "needs_refresh": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        },
        "required": [
          "groupUpdated",
          "needs_refresh"
        ]
      },
      "GroupNameRequest": {
        "type": "object",
        "properties": {
          "groupjid": {
            "type": "string",
            "description": "Identificador do grupo terminado em @g.us.",
            "minLength": 1
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        },
        "required": [
          "groupjid",
          "name"
        ]
      },
      "GroupImageRequest": {
        "type": "object",
        "properties": {
          "groupjid": {
            "type": "string",
            "description": "Identificador do grupo terminado em @g.us.",
            "minLength": 1
          },
          "image": {
            "type": "string",
            "description": "Data URI/base64/URL permitida (JPEG, PNG, GIF; até 8 MiB), ou remove/delete. Convertida para JPEG até 640×640.",
            "minLength": 1
          }
        },
        "required": [
          "groupjid",
          "image"
        ]
      },
      "GroupUpdateResult": {
        "type": "object",
        "properties": {
          "response": {
            "type": "string"
          },
          "needs_refresh": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "pictureId": {
            "type": "string",
            "description": "Presente na atualização de imagem."
          }
        },
        "required": [
          "response",
          "needs_refresh"
        ]
      },
      "ProxyRequest": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "custom",
              "none",
              "internal"
            ]
          },
          "proxy_url": {
            "type": "string",
            "description": "Obrigatória em custom; http/https/socks5/socks5h; sondada antes da gravação.",
            "writeOnly": true
          },
          "confirm_no_proxy": {
            "type": "boolean",
            "description": "Obrigatório true em mode=none."
          }
        },
        "required": [
          "mode"
        ],
        "allOf": [
          {
            "if": {
              "properties": {
                "mode": {
                  "const": "custom"
                }
              }
            },
            "then": {
              "required": [
                "proxy_url"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "mode": {
                  "const": "none"
                }
              }
            },
            "then": {
              "required": [
                "confirm_no_proxy"
              ],
              "properties": {
                "confirm_no_proxy": {
                  "const": true
                }
              }
            }
          }
        ]
      },
      "Proxy": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "custom",
              "none"
            ]
          },
          "effective_mode": {
            "type": "string",
            "enum": [
              "custom",
              "none"
            ]
          },
          "effective_detail": {
            "type": "string",
            "enum": [
              "direct",
              "instance",
              "sealed"
            ]
          },
          "fallback": {
            "type": "object",
            "properties": {
              "active": {
                "type": "boolean",
                "enum": [
                  false
                ]
              },
              "reason": {
                "type": "string"
              },
              "since": {
                "type": "integer"
              }
            }
          },
          "proxy_url": {
            "type": "string",
            "description": "Credenciais redigidas."
          },
          "proxy_fallback": {
            "type": "string",
            "enum": [
              "never"
            ]
          },
          "managed": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "last_test_at": {
            "type": "integer",
            "description": "Unix em milissegundos."
          },
          "last_test_error": {
            "type": "string"
          },
          "validation_error": {
            "type": "boolean"
          }
        },
        "required": [
          "mode",
          "effective_mode",
          "fallback",
          "proxy_url"
        ]
      },
      "ProxyResult": {
        "type": "object",
        "properties": {
          "details": {
            "type": "string"
          },
          "proxy": {
            "$ref": "#/components/schemas/Proxy"
          },
          "restart_requested": {
            "type": "boolean"
          }
        },
        "required": [
          "details",
          "proxy",
          "restart_requested"
        ]
      },
      "WebhookRequest": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "add",
              "update",
              "delete",
              "inherit"
            ],
            "default": "add"
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "url": {
            "type": "string",
            "description": "HTTPS público, sem credenciais/redirects; exceções de laboratório na allowlist.",
            "maxLength": 2048
          },
          "events": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "Entregues: connection, messages, messages_update, messages_edit, groups, limits, undecryptable, call. qrcode, history e chats são aceitos e ignorados, assim como os demais nomes de compatibilidade documentados."
          },
          "excludeMessages": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "wasSentByApi, wasNotSentByApi, fromMeYes, fromMeNo, isGroupYes, isGroupNo."
          },
          "addUrlEvents": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Deve ser false."
          },
          "addUrlTypesMessages": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Deve ser false."
          },
          "secret": {
            "type": "string",
            "description": "Segredo de assinatura do webhook; até 256 ASCII sem espaços. Nunca é devolvido.",
            "maxLength": 256,
            "writeOnly": true
          }
        },
        "anyOf": [
          {
            "required": [
              "action"
            ],
            "properties": {
              "action": {
                "enum": [
                  "delete",
                  "inherit"
                ]
              }
            }
          },
          {
            "required": [
              "url",
              "events"
            ]
          }
        ]
      },
      "DeliveryStatus": {
        "type": "object",
        "properties": {
          "consecutiveFailures": {
            "type": "integer"
          },
          "lastStatus": {
            "type": "integer"
          },
          "lastError": {
            "type": "string"
          },
          "paused": {
            "type": "boolean"
          },
          "pausedUntil": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "lastDeliveredAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "pending": {
            "type": "integer"
          },
          "dead": {
            "type": "integer"
          },
          "oldestPendingSeconds": {
            "type": "integer"
          },
          "statsError": {
            "type": "boolean"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "registered": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "excludeMessages": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "hasSecret": {
            "type": "boolean"
          },
          "secretSealed": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "deliveryStatus": {
            "$ref": "#/components/schemas/DeliveryStatus"
          },
          "ignoredEvents": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "webhooks": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "inheritedGlobal": {
            "type": "boolean",
            "description": "Indica que a inscrição segue o webhook global do servidor."
          }
        },
        "required": [
          "registered"
        ]
      },
      "WebhookWriteResult": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "properties": {
              "success": {
                "type": "boolean",
                "enum": [
                  true
                ]
              },
              "deleted": {
                "type": "boolean",
                "enum": [
                  true
                ]
              }
            },
            "required": [
              "success",
              "deleted"
            ]
          }
        ]
      },
      "WebhookErrors": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "Falhas recentes do worker, sem payload."
            }
          },
          "worker": {
            "type": "string"
          },
          "lastStatus": {
            "type": "integer"
          },
          "lastError": {
            "type": "string"
          },
          "consecutiveFailures": {
            "type": "integer"
          }
        },
        "required": [
          "errors",
          "worker"
        ]
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "seq": {
            "type": "integer"
          },
          "type": {
            "type": "string"
          },
          "payload": {
            "type": "object",
            "description": "Evento interno; o envelope HTTP entregue ao consumidor é uma projeção. Ver EVENTS.md."
          },
          "createdAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "attempts": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "dead"
            ]
          },
          "deliveredAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "deadAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "lastAttemptAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "lastStatus": {
            "type": "integer"
          },
          "lastError": {
            "type": "string"
          },
          "nextAttemptAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "seq",
          "type",
          "payload",
          "createdAt",
          "attempts",
          "status"
        ]
      },
      "EventsResult": {
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "events"
        ]
      },
      "ReplayResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "event": {
            "$ref": "#/components/schemas/Event"
          }
        },
        "required": [
          "success",
          "event"
        ]
      },
      "AdminRestartResult": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "worker": {
            "type": "string"
          }
        },
        "required": [
          "message",
          "worker"
        ]
      },
      "GlobalWebhookRequest": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "add",
              "update",
              "delete"
            ],
            "default": "add"
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "url": {
            "type": "string",
            "description": "HTTPS público, sem credenciais/redirects; exceções de laboratório na allowlist.",
            "maxLength": 2048
          },
          "events": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "Entregues: connection, messages, messages_update, messages_edit, groups, limits, undecryptable, call. qrcode, history e chats são aceitos e ignorados, assim como os demais nomes de compatibilidade documentados."
          },
          "excludeMessages": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "wasSentByApi, wasNotSentByApi, fromMeYes, fromMeNo, isGroupYes, isGroupNo."
          },
          "addUrlEvents": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Deve ser false."
          },
          "addUrlTypesMessages": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string",
                "enum": [
                  "true",
                  "false",
                  "1",
                  "0",
                  ""
                ]
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Deve ser false."
          },
          "secret": {
            "type": "string",
            "description": "Segredo de assinatura do webhook; até 256 ASCII sem espaços. Nunca é devolvido.",
            "maxLength": 256,
            "writeOnly": true
          }
        },
        "anyOf": [
          {
            "required": [
              "action"
            ],
            "properties": {
              "action": {
                "const": "delete"
              }
            }
          },
          {
            "required": [
              "url",
              "events"
            ]
          }
        ]
      },
      "GlobalWebhook": {
        "type": "object",
        "properties": {
          "registered": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "excludeMessages": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "hasSecret": {
            "type": "boolean"
          },
          "secretSealed": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "RFC 3339.",
            "format": "date-time"
          },
          "ignoredEvents": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scope": {
            "type": "string",
            "const": "server"
          }
        },
        "required": [
          "registered",
          "scope"
        ]
      },
      "MakeCallRequest": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "minLength": 1,
            "description": "Telefone com DDI (formatação removida pela API) ou JID de conversa direta. Resolve as duas formas do nono dígito brasileiro."
          }
        },
        "required": [
          "number"
        ]
      },
      "CallActionRequest": {
        "type": "object",
        "properties": {
          "callId": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "callId"
        ]
      },
      "Call": {
        "type": "object",
        "properties": {
          "callId": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "number": {
            "type": "string",
            "description": "Identidade telefônica canônica, quando conhecida."
          },
          "jid": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "ringing",
              "connecting",
              "active",
              "ended"
            ]
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "answeredAt": {
            "type": "string",
            "format": "date-time"
          },
          "endedAt": {
            "type": "string",
            "format": "date-time"
          },
          "endReason": {
            "type": "string",
            "description": "hangup, rejected, ring_timeout, closed ou motivo do motor."
          },
          "endedBy": {
            "type": "string",
            "enum": [
              "local",
              "remote"
            ]
          }
        },
        "required": [
          "callId",
          "direction",
          "number",
          "status",
          "startedAt"
        ]
      },
      "ActiveCalls": {
        "type": "object",
        "properties": {
          "calls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Call"
            }
          }
        },
        "required": [
          "calls"
        ]
      }
    }
  }
}
