{
  "openapi": "3.0.3",
  "info": {
    "title": "Movvi Pay — API efetiva (etapas 2 a 6)",
    "version": "0.6.0",
    "description": "Especificação do que está REALMENTE implementado em services/api, conferida contra a API em execução por `npm run backend:contract`. Cobre identidade e cadastro (etapa 2), cobranças, checkout e avisos do provedor (etapa 3), livro de lançamentos e tarifas versionadas (etapa 4), saques, devoluções, revisão e comprovantes (etapa 5) e chaves de API, avisos ao lojista, notificações e atendimento (etapa 6). Nenhum provedor de pagamentos está contratado; o único adaptador é o de simulação, que não gera Pix pagável nem transfere valor. /health responde payments_enabled: false."
  },
  "servers": [
    {
      "url": "/",
      "description": "Raiz do serviço. Em desenvolvimento a API escuta 127.0.0.1:4000 e o frontend em localhost:3000 encaminha /api para ela."
    }
  ],
  "tags": [
    {
      "name": "health",
      "description": "Verificação de disponibilidade."
    },
    {
      "name": "auth",
      "description": "Identidade, sessão e recuperação."
    },
    {
      "name": "sellers",
      "description": "Cadastro do vendedor."
    },
    {
      "name": "verification",
      "description": "Envio de dados para verificação."
    },
    {
      "name": "audit",
      "description": "Registro imutável de eventos."
    },
    {
      "name": "charges",
      "description": "Cobranças do vendedor."
    },
    {
      "name": "checkout",
      "description": "Dados públicos para pagar uma cobrança."
    },
    {
      "name": "providers",
      "description": "Recebimento de avisos do provedor."
    },
    {
      "name": "money",
      "description": "Saldo, extrato e tarifas."
    },
    {
      "name": "withdrawals",
      "description": "Saques do vendedor."
    },
    {
      "name": "refunds",
      "description": "Devoluções de pagamentos."
    },
    {
      "name": "admin",
      "description": "Área do operador da plataforma."
    },
    {
      "name": "keys",
      "description": "Chaves de acesso do vendedor."
    },
    {
      "name": "webhooks",
      "description": "Avisos automáticos ao lojista."
    },
    {
      "name": "notifications",
      "description": "Novidades da conta."
    },
    {
      "name": "support",
      "description": "Atendimento."
    }
  ],
  "security": [
    {
      "SessionCookie": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Disponibilidade do serviço e do banco",
        "security": [],
        "responses": {
          "200": {
            "description": "Serviço no ar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                },
                "example": {
                  "status": "ok",
                  "mode": "local",
                  "payments_enabled": false,
                  "provider": "simulation",
                  "origin": "http://localhost:3000"
                }
              }
            }
          }
        },
        "operationId": "healthCheck"
      }
    },
    "/api/v1/auth/register": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Criar conta",
        "description": "Responde sempre a mesma mensagem, exista ou não a conta, para não revelar cadastros. Quando o e-mail é novo, enfileira o link de confirmação na caixa local.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/Accepted"
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "registerAccount"
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Entrar",
        "description": "Cria sessão opaca no servidor e devolve o cookie movvi_session (HttpOnly, SameSite=Lax, Secure quando a origem é HTTPS). O csrf_token da resposta deve acompanhar as alterações autenticadas no cabeçalho X-CSRF-Token. Quando a conta tem segundo fator confirmado, responde 200 com second_factor_required e um desafio, sem criar sessao; a entrada termina em POST /auth/second-factor.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sessão criada.",
            "headers": {
              "Set-Cookie": {
                "description": "movvi_session=<64 hex>; Path=/; HttpOnly; SameSite=Lax; Max-Age=43200",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionUser"
                }
              }
            }
          },
          "401": {
            "description": "Credenciais inválidas ou conta desativada. A mesma resposta é usada nos três casos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "login"
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Sair",
        "description": "Revoga a sessão atual e limpa o cookie. Não tem corpo, mas ainda assim exige Content-Type: application/json.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContentType"
          },
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "responses": {
          "204": {
            "description": "Sessão revogada. Sem corpo."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenMutation"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "logout"
      }
    },
    "/api/v1/auth/session": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Sessão atual e vínculos",
        "description": "Renova o carimbo de atividade da sessão. A sessão expira em 12 horas de duração máxima ou 30 minutos sem atividade.",
        "responses": {
          "200": {
            "description": "Sessão válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionState"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "getSession"
      }
    },
    "/api/v1/auth/verify-email": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Confirmar e-mail",
        "description": "Consome o token do link de confirmação. Tokens são de uso único e expiram em 24 horas. O código demonstrativo 123456 não é aceito.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Message"
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "422": {
            "description": "Token inválido, já usado ou expirado (TOKEN_INVALID), ou campos fora do formato (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "verifyEmail"
      }
    },
    "/api/v1/auth/resend-verification": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Reenviar confirmação de e-mail",
        "description": "Exige sessão. Não faz nada quando o e-mail já está confirmado, mas responde igual. Não tem corpo, mas ainda assim exige Content-Type: application/json.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContentType"
          },
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "responses": {
          "202": {
            "$ref": "#/components/responses/Accepted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenMutation"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "resendVerification"
      }
    },
    "/api/v1/auth/password-recovery": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Pedir link de recuperação",
        "description": "Responde sempre a mesma mensagem, exista ou não a conta. O token de redefinição expira em 30 minutos.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "$ref": "#/components/schemas/Email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/Accepted"
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "requestPasswordRecovery"
      }
    },
    "/api/v1/auth/password-reset": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Redefinir senha",
        "description": "Consome o token de recuperação e revoga todas as sessões do usuário.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordResetInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Message"
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "422": {
            "description": "Token inválido, já usado ou expirado (TOKEN_INVALID), ou campos fora do formato (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "resetPassword"
      }
    },
    "/api/v1/sellers": {
      "get": {
        "tags": [
          "sellers"
        ],
        "summary": "Listar os negócios do usuário",
        "description": "Retorna apenas os vendedores em que o usuário tem vínculo. Exige e-mail confirmado.",
        "responses": {
          "200": {
            "description": "Lista de vínculos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SellerListItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "listSellers"
      },
      "post": {
        "tags": [
          "sellers"
        ],
        "summary": "Cadastrar negócio",
        "description": "Cria o vendedor e o vínculo owner do usuário autenticado. O documento é guardado cifrado; a unicidade usa impressão HMAC, então documentos repetidos respondem 409 sem revelar de quem é o cadastro existente.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SellerInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Negócio cadastrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SellerCreated"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "409": {
            "description": "Documento já cadastrado (SELLER_CONFLICT).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "createSeller"
      }
    },
    "/api/v1/sellers/{seller_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "tags": [
          "sellers"
        ],
        "summary": "Detalhe do negócio",
        "description": "Responde 404, e não 403, quando não há vínculo, para não revelar a existência do registro.",
        "responses": {
          "200": {
            "description": "Dados do vendedor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seller"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "getSeller"
      },
      "patch": {
        "tags": [
          "sellers"
        ],
        "summary": "Alterar o nome da loja",
        "description": "Único campo alterável nesta etapa. Exige vínculo owner e expected_version igual à versão atual; divergência responde 409.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SellerPatchInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Alteração aplicada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SellerPatched"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/VersionConflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "updateSeller"
      }
    },
    "/api/v1/sellers/{seller_id}/verification": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "tags": [
          "verification"
        ],
        "summary": "Histórico de envios de verificação",
        "description": "Até 20 registros, da versão mais recente para a mais antiga. Exige vínculo owner. provider_connected é sempre false nesta etapa: não existe análise externa.",
        "responses": {
          "200": {
            "description": "Envios registrados.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "provider_connected"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/VerificationCase"
                      }
                    },
                    "provider_connected": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "listVerificationCases"
      }
    },
    "/api/v1/sellers/{seller_id}/verification-submissions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "post": {
        "tags": [
          "verification"
        ],
        "summary": "Enviar dados para verificação",
        "description": "Aceito somente quando o vendedor está em draft ou needs_changes. Muda a situação para verification_pending e incrementa a versão. NÃO representa aprovação financeira: nenhum arquivo é transmitido e não há análise externa.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerificationSubmissionInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Envio registrado localmente.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationSubmitted"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Versão divergente (VERSION_CONFLICT) ou situação que não permite novo envio (INVALID_STATE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "submitVerification"
      }
    },
    "/api/v1/sellers/{seller_id}/audit-events": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "tags": [
          "audit"
        ],
        "summary": "Eventos de auditoria do vendedor",
        "description": "50 registros por página, do mais recente para o mais antigo. Exige vínculo owner. A tabela bloqueia UPDATE, DELETE e TRUNCATE por gatilhos. A paginação por offset é do protótipo local; trocar por cursor antes de expor a API externamente.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de eventos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_offset"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AuditEvent"
                      }
                    },
                    "next_offset": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Próximo offset, ou null quando a página não encheu."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "listAuditEvents"
      }
    },
    "/api/v1/sellers/{seller_id}/charges": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listCharges",
        "tags": [
          "charges"
        ],
        "summary": "Listar cobranças do vendedor",
        "description": "50 por página, da mais recente para a mais antiga.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChargeStatusFilter"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de cobranças.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_offset"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Charge"
                      }
                    },
                    "next_offset": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createCharge",
        "tags": [
          "charges"
        ],
        "summary": "Criar cobrança",
        "description": "Copia a tarifa vigente para a cobrança, pede o código ao adaptador de provedor e devolve a URL do checkout. Enquanto o adaptador for o de simulação, nada é pagável.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChargeInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Cobrança criada, ou a primeira resposta repetida pela chave de idempotência.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeCreated"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Chave de idempotência reutilizada com outros dados (IDEMPOTENCY_MISMATCH).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Campos fora do formato (VALIDATION_ERROR), valor que não cobre a tarifa (AMOUNT_BELOW_FEE) ou chave de idempotência mal formada (IDEMPOTENCY_KEY_INVALID).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/charges/{charge_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        },
        {
          "$ref": "#/components/parameters/ChargeId"
        }
      ],
      "get": {
        "operationId": "getCharge",
        "tags": [
          "charges"
        ],
        "summary": "Detalhe da cobrança",
        "description": "Responde 404 quando a cobrança é de outro vendedor, sem revelar que ela existe.",
        "responses": {
          "200": {
            "description": "Cobrança.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/public/checkouts/{token}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CheckoutToken"
        }
      ],
      "get": {
        "operationId": "getPublicCheckout",
        "tags": [
          "checkout"
        ],
        "summary": "Dados públicos do checkout",
        "description": "Sem sessão: é a tela que o comprador abre. Entrega o código apenas enquanto a cobrança está pendente e dentro do prazo.",
        "security": [],
        "responses": {
          "200": {
            "description": "Dados suficientes para pagar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicCheckout"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/providers/{provider}/events": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProviderName"
        }
      ],
      "post": {
        "operationId": "receiveProviderEvent",
        "tags": [
          "providers"
        ],
        "summary": "Receber aviso do provedor",
        "description": "Confere a assinatura antes de olhar o conteúdo e grava o aviso num livro somente-adição. A chave (provedor, event_id) é o que impede aplicar duas vezes quando o mesmo aviso é reenviado. Cobrança só sai de \"pending\", saque só sai de \"processing\" e devolução só sai de \"requested\": aviso fora de ordem não reabre nem re-liquida. `payout.unknown` NÃO devolve o dinheiro reservado — liberar seria arriscar pagar duas vezes; o valor fica reservado e a conciliação passa a apontar.",
        "security": [],
        "x-authentication": "provider-signature",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProviderSignature"
          },
          {
            "$ref": "#/components/parameters/ContentType"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Corpo assinado pelo provedor. É lido como texto e verificado byte a byte antes de qualquer interpretação.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "event_id",
                  "kind",
                  "provider_reference",
                  "occurred_at"
                ],
                "properties": {
                  "event_id": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "charge.paid",
                      "charge.expired",
                      "charge.canceled",
                      "payout.completed",
                      "payout.failed",
                      "payout.unknown",
                      "refund.completed",
                      "refund.failed"
                    ]
                  },
                  "amount_cents": {
                    "type": "integer",
                    "nullable": true
                  },
                  "occurred_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "provider_reference": {
                    "type": "string",
                    "description": "Identificador do recurso no provedor: cobrança, repasse ou devolução."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Aviso recebido. O campo outcome diz o que foi feito; 200 significa registrado, não necessariamente aplicado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProviderEventReceipt"
                }
              }
            }
          },
          "401": {
            "description": "Assinatura ausente, inválida ou corpo alterado depois de assinado (EVENT_UNVERIFIED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Provedor diferente do adaptador configurado (NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/fees/current": {
      "get": {
        "operationId": "getCurrentFees",
        "tags": [
          "money"
        ],
        "summary": "Versão de tarifa vigente",
        "description": "Versões de tarifa são imutáveis e nunca apagadas; mudar tarifa é publicar a próxima. Não há endpoint para publicar: isso é da área administrativa, na etapa 6.",
        "responses": {
          "200": {
            "description": "Tarifas em vigor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeeVersion"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/balance": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "getBalance",
        "tags": [
          "money"
        ],
        "summary": "Saldo do vendedor",
        "description": "Somado a partir do livro de lançamentos a cada consulta. Não existe coluna de saldo que possa divergir do livro.",
        "responses": {
          "200": {
            "description": "Saldo derivado dos lançamentos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/ledger": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listLedgerEntries",
        "tags": [
          "money"
        ],
        "summary": "Extrato do vendedor",
        "description": "Lançamentos das contas do vendedor, 50 por página, do mais recente para o mais antigo. Lançamentos das contas da plataforma não aparecem aqui.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de lançamentos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_offset"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LedgerEntry"
                      }
                    },
                    "next_offset": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/withdrawals": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listWithdrawals",
        "tags": [
          "withdrawals"
        ],
        "summary": "Listar saques do vendedor",
        "responses": {
          "200": {
            "description": "Últimos 50 saques.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Withdrawal"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "requestWithdrawal",
        "tags": [
          "withdrawals"
        ],
        "summary": "Pedir saque",
        "description": "Reserva valor e tarifa no mesmo instante em que grava o pedido: a conferência de saldo e o lançamento acontecem sob trava do vendedor, para que dois pedidos simultâneos não gastem o mesmo dinheiro. O saque entra na fila de revisão. Até withdrawal_review_above_cents o saque segue direto ao provedor e volta como processing; acima disso, ou sem valor de revisão definido, volta como requested e aguarda a fila.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WithdrawalInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Saque pedido, valor reservado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Withdrawal"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Campos fora do formato (VALIDATION_ERROR), abaixo do saque mínimo (WITHDRAWAL_BELOW_MINIMUM), saldo insuficiente para valor e tarifa (INSUFFICIENT_FUNDS) ou limite de 24 horas ultrapassado (DAILY_LIMIT_EXCEEDED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/withdrawals/{withdrawal_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        },
        {
          "$ref": "#/components/parameters/WithdrawalId"
        }
      ],
      "get": {
        "operationId": "getWithdrawal",
        "tags": [
          "withdrawals"
        ],
        "summary": "Detalhe do saque",
        "responses": {
          "200": {
            "description": "Saque.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Withdrawal"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/withdrawals/{withdrawal_id}/receipt": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        },
        {
          "$ref": "#/components/parameters/WithdrawalId"
        }
      ],
      "get": {
        "operationId": "getWithdrawalReceipt",
        "tags": [
          "withdrawals"
        ],
        "summary": "Comprovante do saque",
        "description": "Só existe para saque concluído. Emitir comprovante de operação não concluída é o tipo de documento que confunde quem recebe.",
        "responses": {
          "200": {
            "description": "Comprovante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WithdrawalReceipt"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Saque ainda não concluído (RECEIPT_UNAVAILABLE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/refunds": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listRefunds",
        "tags": [
          "refunds"
        ],
        "summary": "Listar devoluções do vendedor",
        "responses": {
          "200": {
            "description": "Últimas 50 devoluções.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Refund"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "requestRefund",
        "tags": [
          "refunds"
        ],
        "summary": "Pedir devolução",
        "description": "Reserva o valor e entrega o pedido ao provedor. Uma devolução em aberto por cobrança, garantido por índice do banco: dois pedidos simultâneos não passam os dois. A tarifa da cobrança não é devolvida.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefundInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Devolução pedida, valor reservado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refund"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "description": "Cobrança inexistente ou de outro vendedor (NOT_FOUND). Responde igual nos dois casos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Cobrança não paga (CHARGE_NOT_REFUNDABLE) ou já com devolução em análise (REFUND_IN_PROGRESS).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Campos fora do formato (VALIDATION_ERROR), valor acima do restante devolvível (AMOUNT_ABOVE_REMAINING) ou saldo insuficiente (INSUFFICIENT_FUNDS).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/withdrawals": {
      "get": {
        "operationId": "listWithdrawalQueue",
        "tags": [
          "admin"
        ],
        "summary": "Fila de revisão de saques",
        "description": "Exige papel de operador da plataforma, concedido fora da API por linha de comando. Para quem não é operador a rota responde 404, sem revelar que existe.",
        "parameters": [
          {
            "$ref": "#/components/parameters/WithdrawalStatusFilter"
          }
        ],
        "responses": {
          "200": {
            "description": "Saques mais antigos primeiro.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminWithdrawal"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/withdrawals/{withdrawal_id}/decision": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WithdrawalId"
        }
      ],
      "post": {
        "operationId": "decideWithdrawal",
        "tags": [
          "admin"
        ],
        "summary": "Decidir um saque",
        "description": "Recusar devolve valor e tarifa ao disponível e exige motivo. Aprovar apenas entrega o pedido ao provedor: o dinheiro segue reservado até o aviso de resultado chegar. Decidir duas vezes responde 409.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DecisionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saque depois da decisão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Withdrawal"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "O saque já saiu da fila de revisão (ALREADY_DECIDED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Campos fora do formato (VALIDATION_ERROR) ou recusa sem motivo (REASON_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "O provedor não aceitou o repasse (PROVIDER_REFUSED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/api-keys": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listApiKeys",
        "tags": [
          "keys"
        ],
        "summary": "Listar chaves de acesso",
        "responses": {
          "200": {
            "description": "Chaves do vendedor, sem segredo.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createApiKey",
        "tags": [
          "keys"
        ],
        "summary": "Criar chave de acesso",
        "description": "O segredo é devolvido apenas nesta resposta. O servidor guarda só o digest. A chave autentica em Authorization: Bearer e alcanca apenas cobrancas e saldo do proprio vendedor.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 60
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "charges:read",
                        "charges:write",
                        "balance:read"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 3,
                    "description": "Sem escopo declarado a chave nasce so de leitura. Saque, devolucao e gestao de credenciais nao tem escopo: exigem entrada com senha e segundo fator."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Chave criada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreated"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/api-keys/{key_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        },
        {
          "$ref": "#/components/parameters/ApiKeyId"
        }
      ],
      "delete": {
        "operationId": "revokeApiKey",
        "tags": [
          "keys"
        ],
        "summary": "Revogar chave",
        "description": "Revogar é definitivo. Revogar de novo responde 404.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "responses": {
          "204": {
            "description": "Chave revogada."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/webhook": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "getWebhook",
        "tags": [
          "webhooks"
        ],
        "summary": "Destino dos avisos",
        "responses": {
          "200": {
            "description": "Configuração atual e eventos disponíveis.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "endpoint",
                    "available_events"
                  ],
                  "properties": {
                    "endpoint": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/WebhookEndpoint"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "available_events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "saveWebhook",
        "tags": [
          "webhooks"
        ],
        "summary": "Definir destino dos avisos",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Destino salvo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Campos fora do formato (VALIDATION_ERROR) ou destino apontando para rede interna (DESTINATION_REFUSED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/webhook/deliveries": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listWebhookDeliveries",
        "tags": [
          "webhooks"
        ],
        "summary": "Histórico de entregas",
        "description": "Últimas 50 tentativas, com situação, número de tentativas e o erro da última.",
        "responses": {
          "200": {
            "description": "Entregas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/notifications": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listNotifications",
        "tags": [
          "notifications"
        ],
        "summary": "Novidades da conta",
        "parameters": [
          {
            "$ref": "#/components/parameters/UnreadOnly"
          }
        ],
        "responses": {
          "200": {
            "description": "Até 50 novidades, da mais recente para a mais antiga.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "unread"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Notification"
                      }
                    },
                    "unread": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/notifications/read": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "post": {
        "operationId": "markNotificationsRead",
        "tags": [
          "notifications"
        ],
        "summary": "Marcar como lidas",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReadInput"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Marcadas."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/tickets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        }
      ],
      "get": {
        "operationId": "listTickets",
        "tags": [
          "support"
        ],
        "summary": "Listar solicitações",
        "responses": {
          "200": {
            "description": "Solicitações do vendedor.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Ticket"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "openTicket",
        "tags": [
          "support"
        ],
        "summary": "Abrir solicitação",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TicketInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Solicitação aberta com a primeira mensagem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ticket"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/tickets/{ticket_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        },
        {
          "$ref": "#/components/parameters/TicketId"
        }
      ],
      "get": {
        "operationId": "getTicket",
        "tags": [
          "support"
        ],
        "summary": "Conversa da solicitação",
        "responses": {
          "200": {
            "description": "Solicitação com as mensagens em ordem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/sellers/{seller_id}/tickets/{ticket_id}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SellerId"
        },
        {
          "$ref": "#/components/parameters/TicketId"
        }
      ],
      "post": {
        "operationId": "replyTicket",
        "tags": [
          "support"
        ],
        "summary": "Responder na conversa",
        "description": "Mensagens são somente-adição: corrigir é escrever de novo, não reescrever.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Mensagem adicionada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A solicitação já foi encerrada (TICKET_RESOLVED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/metrics": {
      "get": {
        "operationId": "operationalMetrics",
        "tags": [
          "admin"
        ],
        "summary": "Estado operacional das filas e do dinheiro em andamento",
        "description": "Exige papel de operador da plataforma. Para quem não é operador a rota responde 404, sem revelar que existe. Os números contam quanto dinheiro está parado e onde, por isso não são públicos.",
        "responses": {
          "200": {
            "description": "Contagens no instante da consulta e o que passou do limite.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "collected_at",
                    "counters",
                    "queues",
                    "money",
                    "alerts"
                  ],
                  "properties": {
                    "collected_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "counters": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      },
                      "description": "Acumulados desde que o processo subiu; zeram no reinício."
                    },
                    "queues": {
                      "type": "object",
                      "required": [
                        "webhooks_pending",
                        "webhooks_failed",
                        "webhooks_dropped",
                        "webhooks_overdue",
                        "email_pending"
                      ],
                      "properties": {
                        "webhooks_pending": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Avisos aguardando entrega."
                        },
                        "webhooks_failed": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Avisos que falharam e ainda serão tentados."
                        },
                        "webhooks_dropped": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Avisos que esgotaram as tentativas. O lojista não vai receber."
                        },
                        "webhooks_overdue": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Entregas marcadas para o passado e paradas."
                        },
                        "email_pending": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Mensagens ainda não entregues."
                        }
                      }
                    },
                    "money": {
                      "type": "object",
                      "required": [
                        "charges_pending",
                        "withdrawals_awaiting_review",
                        "withdrawals_at_provider",
                        "withdrawals_unknown",
                        "refunds_open"
                      ],
                      "properties": {
                        "charges_pending": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Cobranças aguardando pagamento."
                        },
                        "withdrawals_awaiting_review": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Saques esperando decisão."
                        },
                        "withdrawals_at_provider": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Saques entregues ao provedor."
                        },
                        "withdrawals_unknown": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Saques sem resultado conhecido. Valor segue reservado."
                        },
                        "refunds_open": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Devoluções em andamento."
                        }
                      }
                    },
                    "alerts": {
                      "type": "array",
                      "description": "Vazio quando nenhum limite foi ultrapassado.",
                      "items": {
                        "type": "object",
                        "required": [
                          "metric",
                          "value",
                          "threshold"
                        ],
                        "properties": {
                          "metric": {
                            "type": "string"
                          },
                          "value": {
                            "type": "integer"
                          },
                          "threshold": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/auth/second-factor": {
      "get": {
        "operationId": "secondFactorState",
        "tags": [
          "auth"
        ],
        "summary": "Situacao do segundo fator da conta",
        "responses": {
          "200": {
            "description": "Estado atual.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "enabled",
                    "pending",
                    "required",
                    "recovery_codes_left",
                    "verified_in_session"
                  ],
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "pending": {
                      "type": "boolean",
                      "description": "Comecou o cadastro e nao confirmou. Nao vale nada ate confirmar."
                    },
                    "required": {
                      "type": "boolean",
                      "description": "Verdadeiro para operador da plataforma."
                    },
                    "recovery_codes_left": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "verified_in_session": {
                      "type": "boolean",
                      "description": "Esta sessao provou o segundo fator."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      },
      "post": {
        "operationId": "completeSecondFactor",
        "tags": [
          "auth"
        ],
        "summary": "Segunda etapa da entrada",
        "description": "Recebe o desafio devolvido pelo login e o codigo. A sessao so nasce aqui. O desafio e de uso unico, vale cinco minutos e fecha depois de cinco tentativas.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "challenge",
                  "code"
                ],
                "properties": {
                  "challenge": {
                    "type": "string",
                    "pattern": "^[a-f0-9]{64}$"
                  },
                  "code": {
                    "type": "string",
                    "minLength": 6,
                    "maxLength": 11,
                    "description": "Seis digitos do aplicativo, ou um codigo de recuperacao."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sessao criada; o cookie vem em Set-Cookie.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "user_id",
                    "name",
                    "email",
                    "email_verified",
                    "csrf_token"
                  ],
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string",
                      "format": "email"
                    },
                    "email_verified": {
                      "type": "boolean"
                    },
                    "csrf_token": {
                      "type": "string"
                    },
                    "used_recovery_code": {
                      "type": "boolean"
                    },
                    "recovery_codes_left": {
                      "type": "integer",
                      "minimum": 0
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "security": []
      },
      "delete": {
        "operationId": "disableSecondFactor",
        "tags": [
          "auth"
        ],
        "summary": "Remover o segundo fator",
        "description": "Exige senha e codigo, porque desligar a protecao e o que alguem faria com uma sessao sua aberta. Operador da plataforma nao pode remover.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "password",
                  "code"
                ],
                "properties": {
                  "password": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  },
                  "code": {
                    "type": "string",
                    "minLength": 6,
                    "maxLength": 11
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Removido."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenRole"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ]
      }
    },
    "/api/v1/auth/second-factor/setup": {
      "post": {
        "operationId": "startSecondFactor",
        "tags": [
          "auth"
        ],
        "summary": "Gerar o segredo do segundo fator",
        "description": "O segredo aparece uma vez. Enquanto nao for confirmado, o segundo fator nao vale e a senha continua entrando sozinha.",
        "responses": {
          "200": {
            "description": "Segredo gerado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "secret",
                    "otpauth_url",
                    "notice"
                  ],
                  "properties": {
                    "secret": {
                      "type": "string"
                    },
                    "otpauth_url": {
                      "type": "string"
                    },
                    "notice": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "409": {
            "$ref": "#/components/responses/VersionConflict"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ]
      }
    },
    "/api/v1/auth/second-factor/confirm": {
      "post": {
        "operationId": "confirmSecondFactor",
        "tags": [
          "auth"
        ],
        "summary": "Confirmar o segundo fator e receber os codigos de recuperacao",
        "description": "Pede um codigo gerado pelo aplicativo: sem essa prova alguem ativaria a protecao com um segredo que nunca funcionou e se trancaria fora. Os codigos de recuperacao aparecem so aqui.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 6,
                    "maxLength": 6
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ativado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "recovery_codes",
                    "notice"
                  ],
                  "properties": {
                    "recovery_codes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "notice": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/OriginInvalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/VersionConflict"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMedia"
          },
          "422": {
            "description": "Código do segundo fator inválido (SECOND_FACTOR_INVALID). Não é erro de forma do pedido, então não traz field_errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ]
      }
    },
    "/api/v1/admin/overview": {
      "get": {
        "operationId": "adminOverview",
        "tags": [
          "admin"
        ],
        "summary": "Visão da operação",
        "description": "Contagens e receita no instante da consulta. Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminOverview"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/sellers": {
      "get": {
        "operationId": "adminListSellers",
        "tags": [
          "admin"
        ],
        "summary": "Vendedores da plataforma",
        "description": "Cadastros aguardando análise primeiro. Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_offset"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminSeller"
                      }
                    },
                    "next_offset": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/SellerStatus"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Busca parcial, sem diferenciar maiúsculas. Curingas são tratados como texto.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ]
      }
    },
    "/api/v1/admin/sellers/{seller_id}": {
      "parameters": [
        {
          "name": "seller_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "adminGetSeller",
        "tags": [
          "admin"
        ],
        "summary": "Cadastro de um vendedor",
        "description": "Responsáveis, envios de verificação e saldo. Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminSellerDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/sellers/{seller_id}/decision": {
      "parameters": [
        {
          "name": "seller_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "adminDecideSeller",
        "tags": [
          "admin"
        ],
        "summary": "Decidir cadastro de vendedor",
        "description": "Só vale para cadastro em verification_pending. Aprovar é decisão da plataforma e não substitui o cadastro no provedor. Pedir ajuste permite reenvio; recusar encerra. Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SellerDecisionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cadastro depois da decisão.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SellerDecided"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Versão diferente da lida (VERSION_CONFLICT), cadastro fora de análise (ALREADY_DECIDED) ou sem envio (NO_SUBMISSION).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Campos fora do formato (VALIDATION_ERROR) ou motivo ausente (REASON_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/charges": {
      "get": {
        "operationId": "adminListCharges",
        "tags": [
          "admin"
        ],
        "summary": "Cobranças de todos os vendedores",
        "description": "Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_offset"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminCharge"
                      }
                    },
                    "next_offset": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/ChargeStatusFilter"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Busca parcial, sem diferenciar maiúsculas. Curingas são tratados como texto.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ]
      }
    },
    "/api/v1/admin/fees": {
      "get": {
        "operationId": "adminListFees",
        "tags": [
          "admin"
        ],
        "summary": "Versões de tarifa",
        "description": "Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "current",
                    "versions"
                  ],
                  "properties": {
                    "current": {
                      "type": "object",
                      "required": [
                        "version",
                        "payment_fee_cents",
                        "payment_fee_bps",
                        "withdrawal_fee_cents",
                        "withdrawal_min_cents",
                        "withdrawal_daily_limit_cents",
                        "withdrawal_daily_limit_new_cents",
                        "new_account_days",
                        "withdrawal_review_above_cents"
                      ],
                      "properties": {
                        "version": {
                          "type": "integer"
                        },
                        "payment_fee_cents": {
                          "type": "integer",
                          "description": "Parte fixa da tarifa por venda, em centavos."
                        },
                        "payment_fee_bps": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 1000,
                          "description": "Parte percentual por venda, em pontos-base (99 = 0,99%). Tarifa = fixa + round_half_up(valor × bps / 10000), em centavos."
                        },
                        "withdrawal_fee_cents": {
                          "type": "integer"
                        },
                        "withdrawal_min_cents": {
                          "type": "integer",
                          "description": "Menor saque aceito."
                        },
                        "withdrawal_daily_limit_cents": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Soma de saques numa janela de 24 horas. Nulo é sem limite. Saques recusados ou que falharam não contam."
                        },
                        "withdrawal_daily_limit_new_cents": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Limite em 24 horas enquanto a conta tiver menos de new_account_days dias. Nulo: vale o limite geral."
                        },
                        "new_account_days": {
                          "type": "integer"
                        },
                        "withdrawal_review_above_cents": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Saque até este valor segue direto ao provedor; acima, vai para a fila de revisão. Nulo: todo saque é revisado."
                        }
                      }
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FeeVersionRecord"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "adminPublishFees",
        "tags": [
          "admin"
        ],
        "summary": "Publicar nova versão de tarifa",
        "description": "Cria a próxima versão, em vigor imediatamente. Cobranças e saques existentes mantêm a tarifa copiada. Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeePublishInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Versão publicada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeeVersionRecord"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Outra versão foi publicada depois da lida (VERSION_CONFLICT).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Campos fora do formato (VALIDATION_ERROR), limites contraditórios (LIMITS_INCONSISTENT) ou tarifa abaixo do custo informado (FEE_BELOW_COST).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/tickets": {
      "get": {
        "operationId": "adminListTickets",
        "tags": [
          "admin"
        ],
        "summary": "Solicitações de suporte",
        "description": "Resolvidas por último. Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminTicket"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "in_progress",
                "resolved"
              ]
            }
          }
        ]
      }
    },
    "/api/v1/admin/tickets/{ticket_id}": {
      "parameters": [
        {
          "name": "ticket_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "adminGetTicket",
        "tags": [
          "admin"
        ],
        "summary": "Conversa de suporte",
        "description": "Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminTicketDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/tickets/{ticket_id}/messages": {
      "parameters": [
        {
          "name": "ticket_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "adminAnswerTicket",
        "tags": [
          "admin"
        ],
        "summary": "Responder solicitação",
        "description": "Grava a mensagem como da plataforma e atualiza a situação. Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          },
          {
            "$ref": "#/components/parameters/CsrfToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlatformReplyInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Resposta gravada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "status"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "in_progress",
                        "resolved"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Solicitação já encerrada (TICKET_RESOLVED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/admin/audit-events": {
      "get": {
        "operationId": "adminListAuditEvents",
        "tags": [
          "admin"
        ],
        "summary": "Auditoria da plataforma",
        "description": "Exige papel de operador e segundo fator verificado na sessão. Para quem não é operador responde 404.",
        "responses": {
          "200": {
            "description": "Resposta.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "next_offset"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AdminAuditEvent"
                      }
                    },
                    "next_offset": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "Operador sem segundo fator verificado nesta sessão (SECOND_FACTOR_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "operator"
              ],
              "default": "all"
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ]
      }
    },
    "/api/v1/public/pricing": {
      "get": {
        "operationId": "getPublicPricing",
        "tags": [
          "checkout"
        ],
        "summary": "Tabela de tarifas pública",
        "description": "Política comercial em vigor, sem sessão, para a página inicial. Não inclui o custo do provedor.",
        "security": [],
        "responses": {
          "200": {
            "description": "Versão em vigor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPricing"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "SessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "movvi_session",
        "description": "Sessão opaca criada no login. HttpOnly, SameSite=Lax, Secure quando a origem é HTTPS. Não há autenticação Bearer nesta etapa."
      }
    },
    "parameters": {
      "Origin": {
        "name": "Origin",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Obrigatório em todo método diferente de GET, HEAD e OPTIONS. Precisa ser exatamente PUBLIC_APP_URL; qualquer outro valor responde 403 ORIGIN_INVALID."
      },
      "CsrfToken": {
        "name": "X-CSRF-Token",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Obrigatório nas alterações com sessão. Valor devolvido em csrf_token pelo login ou por GET /api/v1/auth/session."
      },
      "SellerId": {
        "name": "seller_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 0,
          "maximum": 1000000,
          "default": 0
        }
      },
      "ContentType": {
        "name": "Content-Type",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "enum": [
            "application/json"
          ]
        },
        "description": "Obrigatório em toda alteração, inclusive nas que não têm corpo: o servidor lê o corpo antes de rotear e responde 415 JSON_REQUIRED sem este cabeçalho."
      },
      "ChargeId": {
        "name": "charge_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "CheckoutToken": {
        "name": "token",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-f0-9]{32}$"
        },
        "description": "Vai na URL do checkout. Não revela o vendedor nem a cobrança."
      },
      "ProviderName": {
        "name": "provider",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-z0-9_-]{1,40}$"
        },
        "description": "Precisa ser o adaptador configurado; qualquer outro responde 404."
      },
      "ProviderSignature": {
        "name": "X-Provider-Signature",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "HMAC-SHA256 do corpo exato recebido. É o que autentica o aviso: este endpoint não exige Origin nem sessão, porque a chamada vem dos servidores do provedor."
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{16,128}$"
        },
        "description": "Repetir a mesma chave com o mesmo corpo devolve a primeira resposta em vez de criar outra cobrança. A mesma chave com corpo diferente responde 409."
      },
      "ChargeStatusFilter": {
        "name": "status",
        "in": "query",
        "required": false,
        "schema": {
          "$ref": "#/components/schemas/ChargeStatus"
        }
      },
      "WithdrawalId": {
        "name": "withdrawal_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "WithdrawalStatusFilter": {
        "name": "status",
        "in": "query",
        "required": false,
        "schema": {
          "$ref": "#/components/schemas/WithdrawalStatus"
        }
      },
      "ApiKeyId": {
        "name": "key_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "TicketId": {
        "name": "ticket_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "UnreadOnly": {
        "name": "unread",
        "in": "query",
        "required": false,
        "schema": {
          "type": "boolean"
        }
      }
    },
    "responses": {
      "Accepted": {
        "description": "Pedido aceito. A mensagem é neutra e não confirma a existência da conta.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Message"
            }
          }
        }
      },
      "Message": {
        "description": "Operação concluída.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Message"
            }
          }
        }
      },
      "Unauthenticated": {
        "description": "Sem sessão (UNAUTHENTICATED) ou sessão expirada/revogada (SESSION_EXPIRED).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "OriginInvalid": {
        "description": "Origem da requisição não permitida (ORIGIN_INVALID).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ForbiddenMutation": {
        "description": "Origem não permitida (ORIGIN_INVALID) ou X-CSRF-Token ausente/incorreto (CSRF_INVALID).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ForbiddenRole": {
        "description": "Origem, CSRF, e-mail não confirmado ou vínculo sem permissão (FORBIDDEN).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "EmailNotVerified": {
        "description": "E-mail ainda não confirmado (EMAIL_NOT_VERIFIED).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Recurso inexistente ou sem vínculo do usuário (NOT_FOUND).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "VersionConflict": {
        "description": "expected_version diferente da versão atual (VERSION_CONFLICT).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationError": {
        "description": "Campos fora do formato (VALIDATION_ERROR).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        }
      },
      "TooLarge": {
        "description": "Corpo acima de 32768 bytes (BODY_TOO_LARGE).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnsupportedMedia": {
        "description": "Content-Type diferente de application/json (JSON_REQUIRED).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Limite de tentativas atingido (RATE_LIMITED). A janela é de 10 minutos.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Sempre 600."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "Falha interna (INTERNAL_ERROR). Detalhes não são expostos; use request_id para localizar no log.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Health": {
        "type": "object",
        "required": [
          "status",
          "mode",
          "payments_enabled",
          "provider",
          "origin"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "local",
              "test"
            ]
          },
          "payments_enabled": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "provider": {
            "type": "string",
            "description": "Adaptador de provedor em uso. Hoje sempre \"simulation\"."
          },
          "origin": {
            "type": "string",
            "format": "uri",
            "description": "Origem que esta instância aceita em requisições de alteração. Publicada para ferramentas de linha de comando não discordarem da API no ar."
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message",
          "request_id"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "description": "Texto em português do Brasil, pronto para exibição."
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ValidationError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "required": [
              "field_errors"
            ],
            "properties": {
              "field_errors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "field",
                    "message"
                  ],
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "Email": {
        "type": "string",
        "format": "email",
        "maxLength": 254,
        "description": "Normalizado para minúsculas e sem espaços nas pontas."
      },
      "Password": {
        "type": "string",
        "minLength": 12,
        "maxLength": 128,
        "description": "Guardada com scrypt e sal aleatório."
      },
      "Token": {
        "type": "string",
        "pattern": "^[a-f0-9]{64}$",
        "description": "Token do link enviado à caixa local. Uso único."
      },
      "RegisterInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "email",
          "password",
          "name"
        ],
        "properties": {
          "email": {
            "$ref": "#/components/schemas/Email"
          },
          "password": {
            "$ref": "#/components/schemas/Password"
          },
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120
          }
        }
      },
      "LoginInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "$ref": "#/components/schemas/Email"
          },
          "password": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        }
      },
      "TokenInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "$ref": "#/components/schemas/Token"
          }
        }
      },
      "PasswordResetInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token",
          "password"
        ],
        "properties": {
          "token": {
            "$ref": "#/components/schemas/Token"
          },
          "password": {
            "$ref": "#/components/schemas/Password"
          }
        }
      },
      "SessionUser": {
        "type": "object",
        "required": [
          "user_id",
          "name",
          "email",
          "email_verified",
          "csrf_token"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "email_verified": {
            "type": "boolean"
          },
          "csrf_token": {
            "type": "string"
          }
        }
      },
      "SessionState": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SessionUser"
          },
          {
            "type": "object",
            "required": [
              "memberships"
            ],
            "properties": {
              "memberships": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          }
        ]
      },
      "Membership": {
        "type": "object",
        "required": [
          "seller_id",
          "role"
        ],
        "properties": {
          "seller_id": {
            "type": "string",
            "format": "uuid"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner"
            ],
            "description": "Somente owner é criado nesta etapa."
          }
        }
      },
      "SellerStatus": {
        "type": "string",
        "enum": [
          "draft",
          "verification_pending",
          "needs_changes",
          "approved",
          "rejected"
        ],
        "description": "approved é a aprovação da plataforma, não do provedor; nenhuma rota depende dele para liberar pagamento."
      },
      "SellerInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "legal_name",
          "trade_name",
          "tax_id",
          "terms_version"
        ],
        "properties": {
          "legal_name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 160
          },
          "trade_name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120
          },
          "tax_id": {
            "type": "string",
            "pattern": "^\\d{11}$|^\\d{14}$",
            "description": "CPF ou CNPJ apenas com dígitos. Os dígitos verificadores são conferidos. Guardado cifrado com AES-GCM; nunca é devolvido nas respostas."
          },
          "terms_version": {
            "type": "string",
            "enum": [
              "local-v1"
            ],
            "description": "Aviso de teste do ambiente local. Não substitui os termos de produção."
          }
        }
      },
      "SellerCreated": {
        "type": "object",
        "required": [
          "id",
          "status",
          "version"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft"
            ]
          },
          "version": {
            "type": "integer",
            "enum": [
              1
            ]
          }
        }
      },
      "SellerListItem": {
        "type": "object",
        "required": [
          "id",
          "legal_name",
          "trade_name",
          "status",
          "version",
          "role"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "legal_name": {
            "type": "string"
          },
          "trade_name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SellerStatus"
          },
          "version": {
            "type": "integer"
          },
          "role": {
            "type": "string"
          }
        }
      },
      "Seller": {
        "type": "object",
        "required": [
          "id",
          "legal_name",
          "trade_name",
          "status",
          "version",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "legal_name": {
            "type": "string"
          },
          "trade_name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SellerStatus"
          },
          "version": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SellerPatchInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "trade_name",
          "expected_version"
        ],
        "properties": {
          "trade_name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120
          },
          "expected_version": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "SellerPatched": {
        "type": "object",
        "required": [
          "id",
          "trade_name",
          "version"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "trade_name": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          }
        }
      },
      "Segment": {
        "type": "string",
        "enum": [
          "game_items",
          "digital_products",
          "other"
        ]
      },
      "VerificationSubmissionInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "expected_version",
          "business_url",
          "segment",
          "description",
          "consent_version"
        ],
        "properties": {
          "expected_version": {
            "type": "integer",
            "minimum": 1
          },
          "business_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Somente http ou https."
          },
          "segment": {
            "$ref": "#/components/schemas/Segment"
          },
          "description": {
            "type": "string",
            "minLength": 10,
            "maxLength": 2000
          },
          "consent_version": {
            "type": "string",
            "enum": [
              "local-v1"
            ],
            "description": "Aviso de teste do ambiente local."
          }
        }
      },
      "VerificationSubmitted": {
        "type": "object",
        "required": [
          "id",
          "status",
          "provider_connected",
          "message"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "submitted"
            ]
          },
          "provider_connected": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "VerificationCase": {
        "type": "object",
        "required": [
          "id",
          "version",
          "status",
          "business_url",
          "segment",
          "description",
          "consent_version",
          "submitted_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "submitted",
              "needs_changes",
              "approved",
              "rejected"
            ]
          },
          "business_url": {
            "type": "string",
            "format": "uri"
          },
          "segment": {
            "$ref": "#/components/schemas/Segment"
          },
          "description": {
            "type": "string"
          },
          "consent_version": {
            "type": "string"
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time"
          },
          "decision_reason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AuditEvent": {
        "type": "object",
        "required": [
          "id",
          "action",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "description": "Nome do evento, como charge.paid, verification.approved ou fees.published. A lista cresce com o produto."
          },
          "target_id": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ChargeStatus": {
        "type": "string",
        "enum": [
          "pending",
          "paid",
          "expired",
          "canceled"
        ],
        "description": "Situação gravada. Uma cobrança vencida continua \"pending\" até o provedor avisar: quem decide se um pagamento atrasado foi aceito é ele, não o relógio do servidor."
      },
      "ChargeInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "customer_name",
          "product",
          "amount_cents"
        ],
        "properties": {
          "customer_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "product": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "amount_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000000,
            "description": "Em centavos. Precisa ser maior que a tarifa vigente, senão 422 AMOUNT_BELOW_FEE."
          }
        }
      },
      "Charge": {
        "type": "object",
        "required": [
          "id",
          "amount_cents",
          "fee_cents",
          "customer_name",
          "product",
          "status",
          "expired_by_time",
          "checkout_token",
          "provider",
          "provider_charge_id",
          "simulated",
          "expires_at",
          "paid_at",
          "created_at",
          "updated_at",
          "fee_version",
          "refunded_cents"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "amount_cents": {
            "type": "integer"
          },
          "fee_cents": {
            "type": "integer",
            "description": "Tarifa copiada da versão vigente na criação. Mudar a tarifa não altera cobranças já criadas."
          },
          "customer_name": {
            "type": "string"
          },
          "product": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/ChargeStatus"
          },
          "expired_by_time": {
            "type": "boolean",
            "description": "Verdadeiro quando ainda está \"pending\" e já passou de expires_at. Serve para a interface, não muda o estado gravado."
          },
          "checkout_token": {
            "type": "string",
            "pattern": "^[a-f0-9]{32}$"
          },
          "provider": {
            "type": "string"
          },
          "provider_charge_id": {
            "type": "string"
          },
          "simulated": {
            "type": "boolean",
            "description": "Verdadeiro enquanto o adaptador não move dinheiro real."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "paid_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "fee_version": {
            "type": "integer",
            "description": "Versão de tarifa que valia quando a cobrança foi criada. Publicar uma versão nova não altera cobranças existentes."
          },
          "refunded_cents": {
            "type": "integer",
            "description": "Soma das devoluções concluídas. O restante devolvível é amount_cents menos este valor."
          }
        }
      },
      "ChargeCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Charge"
          },
          {
            "type": "object",
            "required": [
              "payload",
              "checkout_url"
            ],
            "properties": {
              "payload": {
                "type": "string",
                "description": "O que o comprador copiaria no aplicativo do banco. Com o adaptador de simulação vem prefixado por MOVVI-SIMULACAO-NAO-PAGAVEL e não é pagável."
              },
              "checkout_url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        ]
      },
      "PublicCheckout": {
        "type": "object",
        "required": [
          "status",
          "amount_cents",
          "product",
          "seller_name",
          "expires_at",
          "payload",
          "payable",
          "simulated"
        ],
        "properties": {
          "status": {
            "$ref": "#/components/schemas/ChargeStatus"
          },
          "amount_cents": {
            "type": "integer"
          },
          "product": {
            "type": "string"
          },
          "seller_name": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "payload": {
            "type": "string",
            "nullable": true,
            "description": "Null quando a cobrança já não pode ser paga."
          },
          "payable": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "simulated": {
            "type": "boolean"
          }
        },
        "description": "Só campos necessários para pagar. Não traz nome do comprador, tarifa, identificador do vendedor nem do provedor."
      },
      "ProviderEventReceipt": {
        "type": "object",
        "required": [
          "received",
          "outcome"
        ],
        "properties": {
          "received": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "outcome": {
            "type": "string",
            "enum": [
              "applied",
              "duplicate",
              "ignored",
              "unknown_charge"
            ],
            "description": "applied: mudou a cobrança. duplicate: o mesmo event_id já estava no livro. ignored: a cobrança não estava mais pendente. unknown_charge: nenhuma cobrança corresponde."
          }
        }
      },
      "LedgerAccount": {
        "type": "string",
        "enum": [
          "seller_available",
          "seller_reserved"
        ],
        "description": "Contas do vendedor. As contas da plataforma (platform_fees, provider_clearing) existem no livro mas nunca aparecem para o vendedor."
      },
      "Balance": {
        "type": "object",
        "required": [
          "available_cents",
          "reserved_cents",
          "currency",
          "as_of"
        ],
        "properties": {
          "available_cents": {
            "type": "integer",
            "description": "Soma dos lançamentos em seller_available. Não é coluna: é sempre calculado."
          },
          "reserved_cents": {
            "type": "integer",
            "description": "Soma dos lançamentos em seller_reserved: valor comprometido em saques e devoluções ainda não finalizados. Saque com resultado desconhecido permanece aqui."
          },
          "currency": {
            "type": "string",
            "enum": [
              "BRL"
            ]
          },
          "as_of": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LedgerEntry": {
        "type": "object",
        "required": [
          "id",
          "account",
          "amount_cents",
          "currency",
          "kind",
          "source_kind",
          "source_id",
          "occurred_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "account": {
            "$ref": "#/components/schemas/LedgerAccount"
          },
          "amount_cents": {
            "type": "integer",
            "description": "Positivo credita, negativo debita. Nunca zero."
          },
          "currency": {
            "type": "string",
            "enum": [
              "BRL"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "charge_settled",
              "withdrawal_requested",
              "withdrawal_completed",
              "withdrawal_released",
              "refund_requested",
              "refund_completed",
              "refund_released"
            ]
          },
          "source_kind": {
            "type": "string",
            "enum": [
              "charge",
              "withdrawal",
              "refund"
            ]
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "Quando o fato aconteceu, segundo o aviso do provedor."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Quando o lançamento entrou no livro."
          }
        }
      },
      "FeeVersion": {
        "type": "object",
        "description": "Política comercial em vigor. Não inclui o custo do provedor.",
        "required": [
          "version",
          "payment_fee_cents",
          "payment_fee_bps",
          "withdrawal_fee_cents",
          "withdrawal_min_cents",
          "withdrawal_daily_limit_cents",
          "withdrawal_daily_limit_new_cents",
          "new_account_days",
          "withdrawal_review_above_cents",
          "currency"
        ],
        "properties": {
          "version": {
            "type": "integer"
          },
          "payment_fee_cents": {
            "type": "integer",
            "description": "Parte fixa da tarifa por venda, em centavos."
          },
          "payment_fee_bps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000,
            "description": "Parte percentual por venda, em pontos-base (99 = 0,99%). Tarifa = fixa + round_half_up(valor × bps / 10000), em centavos."
          },
          "withdrawal_fee_cents": {
            "type": "integer"
          },
          "withdrawal_min_cents": {
            "type": "integer",
            "description": "Menor saque aceito."
          },
          "withdrawal_daily_limit_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Soma de saques numa janela de 24 horas. Nulo é sem limite. Saques recusados ou que falharam não contam."
          },
          "withdrawal_daily_limit_new_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Limite em 24 horas enquanto a conta tiver menos de new_account_days dias. Nulo: vale o limite geral."
          },
          "new_account_days": {
            "type": "integer"
          },
          "withdrawal_review_above_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Saque até este valor segue direto ao provedor; acima, vai para a fila de revisão. Nulo: todo saque é revisado."
          },
          "currency": {
            "type": "string",
            "enum": [
              "BRL"
            ]
          }
        }
      },
      "WithdrawalStatus": {
        "type": "string",
        "enum": [
          "requested",
          "rejected",
          "processing",
          "completed",
          "failed",
          "unknown"
        ],
        "description": "requested: aguardando revisão, valor reservado. rejected: recusado, valor devolvido. processing: entregue ao provedor, valor ainda reservado. completed: pago, com comprovante. failed: provedor recusou, valor devolvido. unknown: provedor aceitou e o resultado se perdeu — o valor SEGUE reservado, de propósito."
      },
      "WithdrawalInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "amount_cents",
          "pix_key"
        ],
        "properties": {
          "amount_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000000
          },
          "pix_key": {
            "type": "string",
            "minLength": 5,
            "maxLength": 100,
            "description": "Chave de destino. Nesta etapa nenhum valor é transferido."
          }
        }
      },
      "Withdrawal": {
        "type": "object",
        "required": [
          "id",
          "amount_cents",
          "fee_cents",
          "fee_version",
          "total_debited_cents",
          "pix_key",
          "status",
          "decision_reason",
          "provider",
          "receipt_number",
          "requested_at",
          "decided_at",
          "finished_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "amount_cents": {
            "type": "integer"
          },
          "fee_cents": {
            "type": "integer",
            "description": "Tarifa de saque copiada da versão vigente no pedido."
          },
          "fee_version": {
            "type": "integer"
          },
          "total_debited_cents": {
            "type": "integer",
            "description": "Valor mais tarifa. É o que sai do disponível na hora do pedido."
          },
          "pix_key": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/WithdrawalStatus"
          },
          "decision_reason": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "nullable": true
          },
          "receipt_number": {
            "type": "string",
            "nullable": true,
            "description": "Só existe quando o saque é concluído."
          },
          "requested_at": {
            "type": "string",
            "format": "date-time"
          },
          "decided_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "WithdrawalReceipt": {
        "type": "object",
        "required": [
          "receipt_number",
          "withdrawal_id",
          "seller_name",
          "seller_legal_name",
          "amount_cents",
          "fee_cents",
          "total_debited_cents",
          "pix_key",
          "requested_at",
          "finished_at",
          "currency",
          "simulated",
          "notice"
        ],
        "properties": {
          "receipt_number": {
            "type": "string"
          },
          "withdrawal_id": {
            "type": "string",
            "format": "uuid"
          },
          "seller_name": {
            "type": "string"
          },
          "seller_legal_name": {
            "type": "string"
          },
          "amount_cents": {
            "type": "integer"
          },
          "fee_cents": {
            "type": "integer"
          },
          "total_debited_cents": {
            "type": "integer"
          },
          "pix_key": {
            "type": "string"
          },
          "requested_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          },
          "currency": {
            "type": "string",
            "enum": [
              "BRL"
            ]
          },
          "simulated": {
            "type": "boolean"
          },
          "notice": {
            "type": "string",
            "description": "Aviso que precisa aparecer no documento enquanto o ambiente for simulado."
          }
        }
      },
      "RefundInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "charge_id",
          "amount_cents",
          "reason"
        ],
        "properties": {
          "charge_id": {
            "type": "string",
            "format": "uuid"
          },
          "amount_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000000,
            "description": "Não pode passar do que resta devolver da cobrança. A tarifa da cobrança não volta."
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          }
        }
      },
      "Refund": {
        "type": "object",
        "required": [
          "id",
          "charge_id",
          "amount_cents",
          "reason",
          "status",
          "provider",
          "receipt_number",
          "requested_at",
          "finished_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "charge_id": {
            "type": "string",
            "format": "uuid"
          },
          "amount_cents": {
            "type": "integer"
          },
          "reason": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "requested",
              "completed",
              "failed"
            ],
            "description": "requested reserva o valor; completed abate da cobrança; failed devolve ao disponível."
          },
          "provider": {
            "type": "string",
            "nullable": true
          },
          "receipt_number": {
            "type": "string",
            "nullable": true
          },
          "requested_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "DecisionInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "decision"
        ],
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "approve",
              "reject"
            ]
          },
          "reason": {
            "type": "string",
            "maxLength": 500,
            "description": "Obrigatório na recusa."
          }
        }
      },
      "AdminWithdrawal": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Withdrawal"
          },
          {
            "type": "object",
            "required": [
              "seller_id",
              "seller_name"
            ],
            "properties": {
              "seller_id": {
                "type": "string",
                "format": "uuid"
              },
              "seller_name": {
                "type": "string"
              }
            }
          }
        ]
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "id",
          "name",
          "prefix",
          "created_at",
          "last_used_at",
          "revoked_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "Começo do segredo, para reconhecer a chave sem revelá-la. O segredo completo nunca é devolvido de novo."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ApiKeyCreated": {
        "type": "object",
        "required": [
          "id",
          "name",
          "prefix",
          "secret",
          "notice"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "secret": {
            "type": "string",
            "description": "Aparece uma vez só. O servidor guarda apenas o digest, então não há como mostrá-lo de novo. Nesta etapa ainda não autentica chamadas."
          },
          "notice": {
            "type": "string"
          }
        }
      },
      "WebhookInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Endereços que resolvem para rede interna são recusados, na configuração e de novo na hora de entregar."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            }
          },
          "enabled": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "url",
          "events",
          "enabled",
          "updated_at"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "description": "O segredo de assinatura nunca é devolvido."
      },
      "WebhookDelivery": {
        "type": "object",
        "required": [
          "id",
          "event",
          "status",
          "attempts",
          "last_status_code",
          "last_error",
          "created_at",
          "delivered_at",
          "next_attempt_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "event": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed",
              "dropped"
            ],
            "description": "pending ainda será tentado; failed esgotou as seis tentativas; dropped perdeu o destino."
          },
          "attempts": {
            "type": "integer"
          },
          "last_status_code": {
            "type": "integer",
            "nullable": true
          },
          "last_error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Notification": {
        "type": "object",
        "required": [
          "id",
          "action",
          "target_id",
          "metadata",
          "created_at",
          "read"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string"
          },
          "target_id": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "read": {
            "type": "boolean"
          }
        },
        "description": "Derivada da auditoria, que já é somente-adição. Só as marcas de leitura são guardadas à parte."
      },
      "ReadInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ids": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "all": {
            "type": "boolean"
          }
        },
        "description": "Informe ids ou all."
      },
      "Ticket": {
        "type": "object",
        "required": [
          "id",
          "subject",
          "category",
          "status",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "subject": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "payments",
              "withdrawals",
              "account",
              "other"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "resolved"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TicketDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ticket"
          },
          {
            "type": "object",
            "required": [
              "messages"
            ],
            "properties": {
              "messages": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "author_side",
                    "body",
                    "created_at"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "author_side": {
                      "type": "string",
                      "enum": [
                        "seller",
                        "platform"
                      ]
                    },
                    "body": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "TicketInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "subject",
          "category",
          "message"
        ],
        "properties": {
          "subject": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "category": {
            "type": "string",
            "enum": [
              "payments",
              "withdrawals",
              "account",
              "other"
            ]
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          }
        }
      },
      "MessageInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          }
        }
      },
      "AdminOverview": {
        "type": "object",
        "required": [
          "sellers",
          "charges",
          "revenue",
          "queues",
          "alerts",
          "payments_enabled"
        ],
        "properties": {
          "sellers": {
            "type": "object",
            "required": [
              "total",
              "by_status"
            ],
            "properties": {
              "total": {
                "type": "integer"
              },
              "by_status": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                }
              }
            }
          },
          "charges": {
            "type": "object",
            "required": [
              "paid_count",
              "paid_cents",
              "pending_count"
            ],
            "properties": {
              "paid_count": {
                "type": "integer"
              },
              "paid_cents": {
                "type": "integer"
              },
              "pending_count": {
                "type": "integer"
              }
            }
          },
          "revenue": {
            "type": "object",
            "description": "Somada da conta platform_fees do livro de lançamentos.",
            "required": [
              "payment_fees_cents",
              "withdrawal_fees_cents",
              "total_cents"
            ],
            "properties": {
              "payment_fees_cents": {
                "type": "integer"
              },
              "withdrawal_fees_cents": {
                "type": "integer"
              },
              "total_cents": {
                "type": "integer"
              }
            }
          },
          "queues": {
            "type": "object",
            "required": [
              "sellers_awaiting_review",
              "withdrawals_awaiting_review",
              "withdrawals_unknown",
              "tickets_open"
            ],
            "properties": {
              "sellers_awaiting_review": {
                "type": "integer"
              },
              "withdrawals_awaiting_review": {
                "type": "integer"
              },
              "withdrawals_unknown": {
                "type": "integer"
              },
              "tickets_open": {
                "type": "integer"
              }
            }
          },
          "alerts": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "metric",
                "value",
                "threshold"
              ],
              "properties": {
                "metric": {
                  "type": "string"
                },
                "value": {
                  "type": "integer"
                },
                "threshold": {
                  "type": "integer"
                }
              }
            }
          },
          "payments_enabled": {
            "type": "boolean"
          }
        }
      },
      "AdminSeller": {
        "type": "object",
        "description": "Documento do vendedor não é devolvido.",
        "required": [
          "id",
          "legal_name",
          "trade_name",
          "status",
          "version",
          "created_at",
          "updated_at",
          "owner_name",
          "owner_email"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "legal_name": {
            "type": "string"
          },
          "trade_name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SellerStatus"
          },
          "version": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "owner_name": {
            "type": "string",
            "nullable": true
          },
          "owner_email": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AdminSellerDetail": {
        "type": "object",
        "required": [
          "id",
          "legal_name",
          "trade_name",
          "status",
          "version",
          "created_at",
          "updated_at",
          "members",
          "verification",
          "balance"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "legal_name": {
            "type": "string"
          },
          "trade_name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SellerStatus"
          },
          "version": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "members": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "email",
                "role",
                "email_verified"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "email": {
                  "type": "string"
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "owner",
                    "finance",
                    "developer",
                    "viewer"
                  ]
                },
                "email_verified": {
                  "type": "boolean"
                }
              }
            }
          },
          "verification": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/VerificationCase"
                },
                {
                  "type": "object",
                  "properties": {
                    "decided_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              ]
            }
          },
          "balance": {
            "type": "object",
            "required": [
              "available_cents",
              "reserved_cents"
            ],
            "properties": {
              "available_cents": {
                "type": "integer"
              },
              "reserved_cents": {
                "type": "integer"
              }
            }
          }
        }
      },
      "SellerDecisionInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "decision",
          "expected_version"
        ],
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "approve",
              "needs_changes",
              "reject"
            ]
          },
          "reason": {
            "type": "string",
            "maxLength": 500,
            "description": "Obrigatório para needs_changes e reject. O vendedor lê este texto."
          },
          "expected_version": {
            "type": "integer",
            "minimum": 1,
            "description": "Versão do cadastro que o operador leu."
          }
        }
      },
      "SellerDecided": {
        "type": "object",
        "required": [
          "id",
          "legal_name",
          "trade_name",
          "status",
          "version",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "legal_name": {
            "type": "string"
          },
          "trade_name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SellerStatus"
          },
          "version": {
            "type": "integer"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AdminCharge": {
        "type": "object",
        "description": "Cobrança vista pela operação. O código do checkout não é devolvido.",
        "required": [
          "id",
          "amount_cents",
          "fee_cents",
          "customer_name",
          "product",
          "status",
          "expired_by_time",
          "provider",
          "provider_charge_id",
          "simulated",
          "expires_at",
          "paid_at",
          "created_at",
          "updated_at",
          "fee_version",
          "refunded_cents",
          "seller_id",
          "seller_name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "amount_cents": {
            "type": "integer"
          },
          "fee_cents": {
            "type": "integer",
            "description": "Tarifa copiada da versão vigente na criação. Mudar a tarifa não altera cobranças já criadas."
          },
          "customer_name": {
            "type": "string"
          },
          "product": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/ChargeStatus"
          },
          "expired_by_time": {
            "type": "boolean",
            "description": "Verdadeiro quando ainda está \"pending\" e já passou de expires_at. Serve para a interface, não muda o estado gravado."
          },
          "provider": {
            "type": "string"
          },
          "provider_charge_id": {
            "type": "string"
          },
          "simulated": {
            "type": "boolean",
            "description": "Verdadeiro enquanto o adaptador não move dinheiro real."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "paid_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "fee_version": {
            "type": "integer",
            "description": "Versão de tarifa que valia quando a cobrança foi criada. Publicar uma versão nova não altera cobranças existentes."
          },
          "refunded_cents": {
            "type": "integer",
            "description": "Soma das devoluções concluídas. O restante devolvível é amount_cents menos este valor."
          },
          "seller_id": {
            "type": "string",
            "format": "uuid"
          },
          "seller_name": {
            "type": "string"
          }
        }
      },
      "FeeVersionRecord": {
        "type": "object",
        "required": [
          "version",
          "payment_fee_cents",
          "payment_fee_bps",
          "withdrawal_fee_cents",
          "withdrawal_min_cents",
          "withdrawal_daily_limit_cents",
          "withdrawal_daily_limit_new_cents",
          "new_account_days",
          "withdrawal_review_above_cents",
          "provider_pix_cost_cents",
          "provider_payout_cost_cents",
          "effective_from",
          "note",
          "created_at"
        ],
        "properties": {
          "version": {
            "type": "integer"
          },
          "payment_fee_cents": {
            "type": "integer",
            "description": "Parte fixa da tarifa por venda, em centavos."
          },
          "payment_fee_bps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000,
            "description": "Parte percentual por venda, em pontos-base (99 = 0,99%). Tarifa = fixa + round_half_up(valor × bps / 10000), em centavos."
          },
          "withdrawal_fee_cents": {
            "type": "integer"
          },
          "withdrawal_min_cents": {
            "type": "integer",
            "description": "Menor saque aceito."
          },
          "withdrawal_daily_limit_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Soma de saques numa janela de 24 horas. Nulo é sem limite. Saques recusados ou que falharam não contam."
          },
          "withdrawal_daily_limit_new_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Limite em 24 horas enquanto a conta tiver menos de new_account_days dias. Nulo: vale o limite geral."
          },
          "new_account_days": {
            "type": "integer"
          },
          "withdrawal_review_above_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Saque até este valor segue direto ao provedor; acima, vai para a fila de revisão. Nulo: todo saque é revisado."
          },
          "provider_pix_cost_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Custo por Pix informado pelo operador ao publicar. Só a operação vê."
          },
          "provider_payout_cost_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Custo por repasse informado pelo operador ao publicar."
          },
          "effective_from": {
            "type": "string",
            "format": "date-time"
          },
          "note": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FeePublishInput": {
        "type": "object",
        "additionalProperties": false,
        "description": "Uma publicação é a política inteira. Quando o custo do provedor é informado, a publicação é recusada se a tarifa da menor venda aceita (R$ 1,00) ou a de saque não cobrir o custo.",
        "required": [
          "payment_fee_cents",
          "payment_fee_bps",
          "withdrawal_fee_cents",
          "withdrawal_min_cents",
          "withdrawal_daily_limit_cents",
          "withdrawal_daily_limit_new_cents",
          "new_account_days",
          "withdrawal_review_above_cents",
          "provider_pix_cost_cents",
          "provider_payout_cost_cents",
          "note",
          "expected_version"
        ],
        "properties": {
          "payment_fee_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "payment_fee_bps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000
          },
          "withdrawal_fee_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "withdrawal_min_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000000
          },
          "withdrawal_daily_limit_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000000,
            "nullable": true
          },
          "withdrawal_daily_limit_new_cents": {
            "type": "integer",
            "minimum": 100,
            "maximum": 100000000,
            "nullable": true
          },
          "new_account_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365
          },
          "withdrawal_review_above_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000000,
            "nullable": true
          },
          "provider_pix_cost_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "nullable": true
          },
          "provider_payout_cost_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "nullable": true
          },
          "note": {
            "type": "string",
            "minLength": 3,
            "maxLength": 300
          },
          "expected_version": {
            "type": "integer",
            "minimum": 1,
            "description": "Versão em vigor que o operador leu."
          }
        }
      },
      "AdminTicket": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ticket"
          },
          {
            "type": "object",
            "required": [
              "seller_id",
              "seller_name"
            ],
            "properties": {
              "seller_id": {
                "type": "string",
                "format": "uuid"
              },
              "seller_name": {
                "type": "string"
              }
            }
          }
        ]
      },
      "AdminTicketDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/TicketDetail"
          },
          {
            "type": "object",
            "required": [
              "seller_id",
              "seller_name"
            ],
            "properties": {
              "seller_id": {
                "type": "string",
                "format": "uuid"
              },
              "seller_name": {
                "type": "string"
              }
            }
          }
        ]
      },
      "PlatformReplyInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "body",
          "status"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "status": {
            "type": "string",
            "enum": [
              "in_progress",
              "resolved"
            ]
          }
        }
      },
      "AdminAuditEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AuditEvent"
          },
          {
            "type": "object",
            "required": [
              "seller_id",
              "seller_name",
              "actor_name",
              "actor_is_operator"
            ],
            "properties": {
              "seller_id": {
                "type": "string",
                "format": "uuid",
                "nullable": true
              },
              "seller_name": {
                "type": "string",
                "nullable": true
              },
              "actor_name": {
                "type": "string",
                "nullable": true
              },
              "actor_is_operator": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "PublicPricing": {
        "allOf": [
          {
            "$ref": "#/components/schemas/FeeVersion"
          },
          {
            "type": "object",
            "required": [
              "payments_enabled"
            ],
            "properties": {
              "payments_enabled": {
                "type": "boolean",
                "description": "Falso enquanto não houver provedor contratado."
              }
            }
          }
        ]
      }
    }
  }
}