{
  "openapi": "3.1.0",
  "info": {
    "title": "Voz Seu Lobato API",
    "version": "1.0.0",
    "description": "API do gateway WhatsApp e do SaaS multi-tenant. Publica sessões, chamadas, gravações, webhooks, widget seguro, login e sessões de conta, seleção multi-organização, membros e convites, gestão de API keys, ciclo de vida de tenants/usuários, catálogo público, planos e entitlements. Pagamentos Mercado Pago são opt-in: exigem VOICE_PAYMENT_PROVIDER=mercadopago, modo sandbox ou production e credenciais correspondentes; production pode iniciar cobranças reais. O live_mode do webhook precisa coincidir exatamente com o modo configurado. Planos cobram preço fixo mensal/anual em BRL, sem cobrança variável por uso. Reembolso ou chargeback posterior atualiza a fatura e bloqueia a assinatura como past_due, preservando o período; não há grace period automático. Sem configuração de provedor, checkout e reconciliação permanecem desabilitados."
  },
  "servers": [
    {
      "url": "https://voz.seulobato.com"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "publicLandingPage",
        "security": [],
        "summary": "Landing pública do Voz Seu Lobato",
        "responses": {
          "200": {
            "description": "HTML público da landing page",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/api-keys": {
      "get": {
        "operationId": "listTenantAPIKeys",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista metadados das API keys",
        "description": "Requer sessão web de tenant_owner ou tenant_admin; tenant deriva da sessão autenticada. Escritas exigem CSRF. Segredo mostrado somente uma vez; servidor armazena apenas hash.",
        "responses": {
          "200": {
            "description": "Chaves sem segredo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/APIKeyMetadata"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente"
          },
          "403": {
            "description": "Papel sem permissão"
          }
        }
      },
      "post": {
        "operationId": "createTenantAPIKey",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Cria API key",
        "description": "Requer sessão web de tenant_owner ou tenant_admin; tenant deriva da sessão autenticada. Escritas exigem CSRF. Segredo mostrado somente uma vez; servidor armazena apenas hash. Escopos aceitos: sessions:read, sessions:write, calls:read, calls:write, recordings:read, webhooks:read, webhooks:write. billing:read não é aceito por esta rota.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "sessions:read",
                        "sessions:write",
                        "calls:read",
                        "calls:write",
                        "recordings:read",
                        "webhooks:read",
                        "webhooks:write"
                      ]
                    },
                    "minItems": 1,
                    "uniqueItems": true
                  },
                  "expires_at": {
                    "type": [
                      "null",
                      "string"
                    ],
                    "format": "date-time"
                  }
                },
                "required": [
                  "label",
                  "scopes"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Chave criada; api_key mostrado uma vez",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "expires_at": {
                      "type": [
                        "null",
                        "string"
                      ],
                      "format": "date-time"
                    },
                    "api_key": {
                      "type": "string"
                    },
                    "shown_once": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "id",
                    "label",
                    "scopes",
                    "expires_at",
                    "api_key",
                    "shown_once"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Label, escopos ou expiração inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente"
          },
          "403": {
            "description": "Papel sem permissão"
          }
        }
      }
    },
    "/api/v1/api-keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "ID da chave no tenant autenticado."
        }
      ],
      "delete": {
        "operationId": "revokeTenantAPIKey",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Revoga API key",
        "description": "Requer sessão web de tenant_owner ou tenant_admin; tenant deriva da sessão autenticada. Escritas exigem CSRF. Segredo mostrado somente uma vez; servidor armazena apenas hash.",
        "responses": {
          "204": {
            "description": "Chave revogada; chave já revogada também retorna 204"
          },
          "401": {
            "description": "Sessão ausente"
          },
          "403": {
            "description": "Papel sem permissão"
          },
          "404": {
            "description": "Chave não encontrada"
          }
        }
      }
    },
    "/api/v1/api-keys/{id}/rotate": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "ID da API key ativa a substituir."
        }
      ],
      "post": {
        "operationId": "rotateTenantAPIKey",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Rotaciona API key transacionalmente",
        "description": "Requer sessão web de tenant_owner ou tenant_admin; tenant deriva da sessão autenticada. Escritas exigem CSRF. Segredo mostrado somente uma vez; servidor armazena apenas hash. Chave antiga é revogada e nova criada na mesma transação. Corpo pode ser omitido ou conter label, scopes e expires_at; campos omitidos são herdados.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "sessions:read",
                        "sessions:write",
                        "calls:read",
                        "calls:write",
                        "recordings:read",
                        "webhooks:read",
                        "webhooks:write"
                      ]
                    },
                    "minItems": 1,
                    "uniqueItems": true
                  },
                  "expires_at": {
                    "type": [
                      "null",
                      "string"
                    ],
                    "format": "date-time"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Nova chave criada; segredo mostrado uma vez",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "replaced_key_id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "expires_at": {
                      "type": [
                        "null",
                        "string"
                      ],
                      "format": "date-time"
                    },
                    "api_key": {
                      "type": "string"
                    },
                    "shown_once": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "id",
                    "replaced_key_id",
                    "label",
                    "scopes",
                    "expires_at",
                    "api_key",
                    "shown_once"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Dados inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente"
          },
          "403": {
            "description": "Papel sem permissão"
          },
          "404": {
            "description": "Chave ativa não encontrada"
          }
        }
      }
    },
    "/api/v1/billing/checkout": {
      "post": {
        "operationId": "createBillingCheckout",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Inicia checkout recorrente de preço fixo para o tenant",
        "description": "Somente tenant_owner. Requer X-CSRF-Token e Idempotency-Key UUID. O provedor deve estar explicitamente configurado em sandbox ou production; confirmação e ativação dependem de webhook assinado e reconciliado.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plan_version_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "plan_version_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout idempotente já existente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "tenant_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "plan_version_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "provider": {
                      "type": "string",
                      "enum": [
                        "mercadopago"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "creating",
                        "pending",
                        "authorized",
                        "cancelled",
                        "failed"
                      ]
                    },
                    "provider_status": {
                      "type": "string"
                    },
                    "checkout_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "format": "uint64"
                    },
                    "currency": {
                      "type": "string",
                      "pattern": "^[A-Z]{3}$"
                    },
                    "cycle": {
                      "type": "string",
                      "enum": [
                        "month",
                        "year"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "id",
                    "tenant_id",
                    "plan_version_id",
                    "provider",
                    "status",
                    "amount_minor",
                    "currency",
                    "cycle",
                    "created_at"
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Checkout recorrente criado no provedor",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "tenant_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "plan_version_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "provider": {
                      "type": "string",
                      "enum": [
                        "mercadopago"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "creating",
                        "pending",
                        "authorized",
                        "cancelled",
                        "failed"
                      ]
                    },
                    "provider_status": {
                      "type": "string"
                    },
                    "checkout_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "format": "uint64"
                    },
                    "currency": {
                      "type": "string",
                      "pattern": "^[A-Z]{3}$"
                    },
                    "cycle": {
                      "type": "string",
                      "enum": [
                        "month",
                        "year"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "id",
                    "tenant_id",
                    "plan_version_id",
                    "provider",
                    "status",
                    "amount_minor",
                    "currency",
                    "cycle",
                    "created_at"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Requisição de checkout inválida"
          },
          "401": {
            "description": "Sessão autenticada de pessoa é necessária"
          },
          "403": {
            "description": "Somente o tenant_owner pode administrar billing; CSRF inválido"
          },
          "404": {
            "description": "Versão de plano não encontrada"
          },
          "409": {
            "description": "Checkout anterior está em estado terminal"
          },
          "503": {
            "description": "Provedor ausente ou configuração inválida"
          },
          "502": {
            "description": "Falha no provedor"
          }
        }
      }
    },
    "/api/v1/billing/entitlements": {
      "get": {
        "operationId": "getTenantEntitlements",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Consulta direitos e limites do plano",
        "description": "Requer sessão web de pessoa com membership ativa tenant_owner no tenant active ou trialing. API keys, tenant_admin, supervisor e operator são negados. O tenant deriva da sessão autenticada.",
        "responses": {
          "200": {
            "description": "Dados do tenant autenticado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingEntitlements"
                }
              }
            }
          },
          "401": {
            "description": "Autenticação ausente/inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "403": {
            "description": "Papel ou escopo insuficiente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "404": {
            "description": "Tenant sem assinatura",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "500": {
            "description": "Falha interna",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/invoices": {
      "get": {
        "operationId": "listTenantInvoices",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista faturas reconciliadas do tenant",
        "description": "Requer sessão web de pessoa com membership ativa tenant_owner no tenant active ou trialing. API keys, tenant_admin, supervisor e operator são negados. O tenant deriva da sessão autenticada.",
        "responses": {
          "200": {
            "description": "Faturas reconciliadas do tenant autenticado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingInvoiceList"
                }
              }
            }
          },
          "401": {
            "description": "Autenticação ausente ou inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "403": {
            "description": "Membership tenant_owner ou estado do tenant insuficiente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "503": {
            "description": "Provedor não configurado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/plans": {
      "get": {
        "operationId": "listTenantBillingPlans",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista planos ativos",
        "description": "Requer sessão web de pessoa com membership ativa tenant_owner no tenant active ou trialing. API keys, tenant_admin, supervisor e operator são negados. O tenant deriva da sessão autenticada.",
        "responses": {
          "200": {
            "description": "Dados do tenant autenticado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingPlanList"
                }
              }
            }
          },
          "401": {
            "description": "Autenticação ausente/inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "403": {
            "description": "Papel ou escopo insuficiente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "404": {
            "description": "Tenant sem assinatura",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "500": {
            "description": "Falha interna",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/portal": {
      "get": {
        "operationId": "getBillingPortal",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Consulta assinatura, checkout pendente e faturas do tenant",
        "description": "Somente tenant_owner. A integração atual usa checkout hospedado e um portal local; não fornece gerenciamento hospedado de cartão.",
        "responses": {
          "200": {
            "description": "Resumo do portal local de cobrança",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "provider": {
                      "type": "string"
                    },
                    "configured": {
                      "type": "boolean"
                    },
                    "billing_portal_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "subscription": {},
                    "pending_checkout": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "tenant_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "plan_version_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "provider": {
                          "type": "string",
                          "enum": [
                            "mercadopago"
                          ]
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "creating",
                            "pending",
                            "authorized",
                            "cancelled",
                            "failed"
                          ]
                        },
                        "provider_status": {
                          "type": "string"
                        },
                        "checkout_url": {
                          "type": "string",
                          "format": "uri"
                        },
                        "amount_minor": {
                          "type": "integer",
                          "format": "uint64"
                        },
                        "currency": {
                          "type": "string",
                          "pattern": "^[A-Z]{3}$"
                        },
                        "cycle": {
                          "type": "string",
                          "enum": [
                            "month",
                            "year"
                          ]
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      },
                      "required": [
                        "id",
                        "tenant_id",
                        "plan_version_id",
                        "provider",
                        "status",
                        "amount_minor",
                        "currency",
                        "cycle",
                        "created_at"
                      ]
                    },
                    "invoices": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "provider_invoice_id": {
                            "type": "string"
                          },
                          "provider_payment_id": {
                            "type": "string"
                          },
                          "subscription_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "amount_minor": {
                            "type": "integer",
                            "format": "uint64"
                          },
                          "currency": {
                            "type": "string",
                            "pattern": "^[A-Z]{3}$"
                          },
                          "invoice_status": {
                            "type": "string"
                          },
                          "payment_status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "debit_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "paid_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        },
                        "required": [
                          "provider_invoice_id",
                          "subscription_id",
                          "amount_minor",
                          "currency",
                          "invoice_status"
                        ]
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "provider",
                    "configured",
                    "invoices",
                    "message"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão autenticada de pessoa é necessária"
          },
          "403": {
            "description": "Somente o tenant_owner pode consultar billing"
          },
          "503": {
            "description": "Estado de cobrança indisponível"
          },
          "502": {
            "description": "Falha no provedor"
          }
        }
      }
    },
    "/api/v1/billing/subscription": {
      "get": {
        "operationId": "getTenantSubscription",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Consulta assinatura do tenant",
        "description": "Requer sessão web de pessoa com membership ativa tenant_owner no tenant active ou trialing. API keys, tenant_admin, supervisor e operator são negados. O tenant deriva da sessão autenticada.",
        "responses": {
          "200": {
            "description": "Dados do tenant autenticado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSubscription"
                }
              }
            }
          },
          "401": {
            "description": "Autenticação ausente/inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "403": {
            "description": "Papel ou escopo insuficiente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "404": {
            "description": "Tenant sem assinatura",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "500": {
            "description": "Falha interna",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/subscription/cancel": {
      "post": {
        "operationId": "cancelBillingSubscription",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Cancela a assinatura recorrente do tenant",
        "description": "Somente tenant_owner. Requer X-CSRF-Token. A requisição cancela a assinatura no provedor e atualiza o estado local; histórico de faturas e períodos já pagos são preservados.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Assinatura cancelada"
          },
          "401": {
            "description": "Sessão autenticada de pessoa é necessária"
          },
          "403": {
            "description": "Somente o tenant_owner pode administrar billing; CSRF inválido"
          },
          "404": {
            "description": "Assinatura atual não encontrada"
          },
          "409": {
            "description": "Estado da assinatura não permite cancelamento"
          },
          "503": {
            "description": "Provedor ausente ou configuração inválida"
          },
          "502": {
            "description": "Falha no provedor"
          }
        }
      }
    },
    "/api/v1/billing/summary": {
      "get": {
        "operationId": "getTenantBillingSummary",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Consulta resumo de faturamento e uso",
        "description": "Requer sessão web de pessoa com membership ativa tenant_owner no tenant active ou trialing. API keys, tenant_admin, supervisor e operator são negados. O tenant deriva da sessão autenticada.",
        "responses": {
          "200": {
            "description": "Dados do tenant autenticado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSummary"
                }
              }
            }
          },
          "401": {
            "description": "Autenticação ausente/inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "403": {
            "description": "Papel ou escopo insuficiente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "404": {
            "description": "Tenant sem assinatura",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "500": {
            "description": "Falha interna",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/usage": {
      "get": {
        "operationId": "getTenantUsageSummary",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Consulta uso do ciclo atual",
        "description": "Requer sessão web de pessoa com membership ativa tenant_owner no tenant active ou trialing. API keys, tenant_admin, supervisor e operator são negados. O tenant deriva da sessão autenticada.",
        "responses": {
          "200": {
            "description": "Dados do tenant autenticado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSummary"
                }
              }
            }
          },
          "401": {
            "description": "Autenticação ausente/inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "403": {
            "description": "Papel ou escopo insuficiente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "404": {
            "description": "Tenant sem assinatura",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "500": {
            "description": "Falha interna",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/calls": {
      "get": {
        "operationId": "listCalls",
        "summary": "Lista chamadas do tenant",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chamadas",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallList"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCall",
        "summary": "Inicia chamada de saída",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Chave URL-safe de 16 a 128 caracteres. Repetir a chave e o mesmo corpo retorna a chamada original sem iniciar outra ligação.",
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9._~-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCall"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Chamada aceita para processamento",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "409": {
            "description": "Sessão desconectada/ocupada, chave reutilizada com outro corpo ou solicitação idempotente ainda em processamento"
          }
        }
      }
    },
    "/api/v1/calls/{id}": {
      "get": {
        "operationId": "getCall",
        "summary": "Consulta estado da chamada",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Chamada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "404": {
            "description": "Não encontrada"
          }
        }
      }
    },
    "/api/v1/calls/{id}/answer": {
      "post": {
        "operationId": "answerCall",
        "summary": "Atende chamada entrante",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "204": {
            "description": "Atendida"
          },
          "409": {
            "description": "Estado incompatível"
          }
        }
      }
    },
    "/api/v1/calls/{id}/hangup": {
      "post": {
        "operationId": "hangupCall",
        "summary": "Encerra chamada",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "204": {
            "description": "Encerrada"
          },
          "409": {
            "description": "Estado incompatível"
          }
        }
      }
    },
    "/api/v1/calls/{id}/media-token": {
      "post": {
        "operationId": "createMediaToken",
        "summary": "Cria token efêmero e de uso único para WSS",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "201": {
            "description": "Token criado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "token",
                    "expires_at"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Chamada sem mídia ativa"
          }
        }
      }
    },
    "/api/v1/calls/{id}/mute": {
      "post": {
        "operationId": "setCallMute",
        "summary": "Define o mudo local do microfone do operador",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "muted": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "muted"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estado local do microfone atualizado"
          },
          "400": {
            "description": "Corpo inválido"
          },
          "409": {
            "description": "Chamada inativa ou não disponível neste tenant"
          }
        }
      }
    },
    "/api/v1/calls/{id}/reject": {
      "post": {
        "operationId": "rejectCall",
        "summary": "Recusa chamada entrante",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "204": {
            "description": "Recusada"
          },
          "409": {
            "description": "Estado incompatível"
          }
        }
      }
    },
    "/api/v1/dialer/origins": {
      "get": {
        "operationId": "listDialerOrigins",
        "summary": "Lista origens HTTPS autorizadas para o widget",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Origens autorizadas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uri-origin"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Chave inválida"
          },
          "403": {
            "description": "Escopo sessions:write ausente"
          }
        }
      },
      "put": {
        "operationId": "allowDialerOrigin",
        "summary": "Autoriza uma origem exata do CRM para incorporar o discador",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "format": "uri-origin",
                    "examples": [
                      "https://crm.example.com"
                    ]
                  }
                },
                "required": [
                  "origin"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Origem autorizada"
          },
          "400": {
            "description": "Origem inválida"
          },
          "401": {
            "description": "Chave inválida"
          },
          "403": {
            "description": "Escopo sessions:write ausente"
          }
        }
      },
      "delete": {
        "operationId": "removeDialerOrigin",
        "summary": "Remove uma origem autorizada",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "origin",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri-origin"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Origem removida"
          },
          "400": {
            "description": "Origem inválida"
          },
          "401": {
            "description": "Chave inválida"
          },
          "403": {
            "description": "Escopo sessions:write ausente"
          }
        }
      }
    },
    "/api/v1/dialer/sessions": {
      "post": {
        "operationId": "createDialerSessionGrant",
        "summary": "Cria grant de uso único limitado a uma sessão e origem para iniciar o widget",
        "description": "Chame do backend do CRM após autorizar o usuário. O token expira em 90 segundos e não deve ser registrado nem armazenado em cache.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "session_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "origin": {
                    "type": "string",
                    "format": "uri-origin"
                  }
                },
                "required": [
                  "session_id",
                  "origin"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Grant temporário criado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "session_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "origin": {
                      "type": "string",
                      "format": "uri-origin"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "token",
                    "session_id",
                    "origin",
                    "expires_at"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Corpo inválido"
          },
          "401": {
            "description": "Chave inválida"
          },
          "403": {
            "description": "Origem não autorizada"
          },
          "404": {
            "description": "Sessão não conectada ou fora do tenant"
          }
        }
      }
    },
    "/api/v1/events-token": {
      "post": {
        "operationId": "createEventsToken",
        "summary": "Cria token de uso único para assinar o WSS de eventos",
        "responses": {
          "201": {
            "description": "Token efêmero válido por 60 segundos"
          },
          "401": {
            "description": "Chave inválida"
          },
          "403": {
            "description": "Escopo calls:read ausente"
          }
        }
      }
    },
    "/api/v1/payments/mercadopago/webhook": {
      "post": {
        "operationId": "receiveMercadoPagoWebhook",
        "security": [],
        "summary": "Reconcilia eventos assinados do Mercado Pago",
        "description": "Valida x-signature, x-request-id e data.id e busca o estado atual da assinatura ou fatura pela API do Mercado Pago antes de atualizar dados locais. VOICE_MERCADOPAGO_MODE aceita sandbox ou production; o live_mode do corpo deve ser false em sandbox e true em production. Eventos de chargeback, reembolso total ou parcial posteriores à aprovação atualizam a fatura e marcam a assinatura correspondente como past_due sem alterar as datas do período. A cobrança é fixa por plano e ciclo, nunca por minuto ou uso.",
        "parameters": [
          {
            "name": "x-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-request-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "data.id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "action",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data",
                  "live_mode"
                ],
                "properties": {
                  "id": {
                    "type": [
                      "string",
                      "integer"
                    ]
                  },
                  "type": {
                    "type": "string"
                  },
                  "action": {
                    "type": "string"
                  },
                  "live_mode": {
                    "type": "boolean"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "id"
                    ],
                    "properties": {
                      "id": {
                        "type": [
                          "string",
                          "integer"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Evento conciliado ou ignorado por não ser suportado"
          },
          "400": {
            "description": "Payload inválido ou live_mode incompatível com o modo configurado"
          },
          "401": {
            "description": "Assinatura HMAC inválida ou expirada"
          },
          "503": {
            "description": "Provedor desabilitado ou reconciliação temporariamente indisponível"
          }
        }
      }
    },
    "/api/v1/platform/audit": {
      "get": {
        "operationId": "listPlatformAuditEvents",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista eventos unificados de auditoria da plataforma",
        "description": "Disponível somente para platform_admin. Agrega eventos de plataforma, faturamento, tenant e conta, ordenados do mais recente. A interface /admin mostra campos operacionais e omite metadata_json.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Quantidade máxima de eventos; padrão 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "tenant_id",
            "in": "query",
            "required": false,
            "description": "Filtra eventos pelo ID da organização; filtro vazio inclui eventos globais.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eventos encontrados; data é uma lista vazia quando não há resultados. metadata_json é o campo de auditoria armazenado; a interface web o omite.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer",
                            "format": "int64"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "platform",
                              "billing",
                              "tenant",
                              "account"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "actor_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "resource_type": {
                            "type": "string"
                          },
                          "resource_id": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string"
                          },
                          "metadata_json": {
                            "type": "string",
                            "description": "Metadados gravados no evento; não exibidos pelo painel web."
                          }
                        },
                        "required": [
                          "id",
                          "source",
                          "created_at",
                          "actor_id",
                          "action",
                          "resource_type",
                          "metadata_json"
                        ]
                      }
                    },
                    "limit": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 200
                    }
                  },
                  "required": [
                    "data",
                    "limit"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Parâmetro limit fora de 1–200 ou tenant_id maior que 64 caracteres"
          },
          "401": {
            "description": "Sessão sem papel platform_admin"
          },
          "503": {
            "description": "Não foi possível ler os eventos de auditoria"
          }
        }
      }
    },
    "/api/v1/platform/plans": {
      "get": {
        "operationId": "listPlatformBillingPlans",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista todos os planos, inclusive inativos",
        "description": "Requer sessão de conta com papel platform_admin. Escritas também passam pela proteção CSRF do browser.",
        "responses": {
          "200": {
            "description": "Planos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingPlanList"
                }
              }
            }
          },
          "401": {
            "description": "Administrador global requerido"
          }
        }
      },
      "post": {
        "operationId": "createPlatformBillingPlan",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Cria plano com versões de preço fixo",
        "description": "Requer sessão de conta com papel platform_admin. Escritas também passam pela proteção CSRF do browser. Ciclo month/year e preço recorrente fixo; métricas de uso não geram preço por unidade ou excedente.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingPlanInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Plano criado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingPlan"
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "401": {
            "description": "Administrador global requerido"
          },
          "409": {
            "description": "Slug ou ciclo já existente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/platform/plans/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "operationId": "updatePlatformBillingPlan",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Atualiza nome, descrição ou status do plano",
        "description": "Requer sessão de conta com papel platform_admin. Escritas também passam pela proteção CSRF do browser.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingPlanUpdate"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Plano atualizado"
          },
          "400": {
            "description": "ID ou entrada inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "401": {
            "description": "Administrador global requerido"
          },
          "404": {
            "description": "Plano não encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/platform/plans/{id}/versions": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "addPlatformBillingPlanVersion",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Adiciona versão de preço fixo",
        "description": "Requer sessão de conta com papel platform_admin. Escritas também passam pela proteção CSRF do browser. Cada ciclo month/year ocorre uma vez por plano; informe os oito limites. null significa ilimitado e zero significa nenhum.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingPlanVersionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Versão criada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingPlanVersion"
                }
              }
            }
          },
          "400": {
            "description": "ID ou entrada inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "401": {
            "description": "Administrador global requerido"
          },
          "404": {
            "description": "Plano não encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "409": {
            "description": "Ciclo já definido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/platform/subscriptions": {
      "get": {
        "operationId": "listPlatformSubscriptions",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista assinaturas para operação global",
        "description": "Requer sessão de conta com papel platform_admin. Escritas também passam pela proteção CSRF do browser.",
        "parameters": [
          {
            "name": "tenant_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filtro opcional pelo tenant."
          }
        ],
        "responses": {
          "200": {
            "description": "Assinaturas, até 500",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlatformSubscription"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "tenant_id inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "401": {
            "description": "Administrador global requerido"
          }
        }
      },
      "post": {
        "operationId": "assignPlatformSubscription",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Associa versão de plano a tenant",
        "description": "Requer sessão de conta com papel platform_admin. Escritas também passam pela proteção CSRF do browser. Registra assinatura; não processa pagamento.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tenant_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "plan_version_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "trialing",
                      "active",
                      "past_due",
                      "suspended",
                      "cancelled"
                    ]
                  }
                },
                "required": [
                  "tenant_id",
                  "plan_version_id",
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Assinatura atribuída",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSubscription"
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "401": {
            "description": "Administrador global requerido"
          },
          "404": {
            "description": "Tenant ou versão não encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "409": {
            "description": "Assinatura atual bloqueia a operação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/platform/subscriptions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "operationId": "updatePlatformSubscription",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Atualiza status da assinatura",
        "description": "Requer sessão de conta com papel platform_admin. Escritas também passam pela proteção CSRF do browser. Não aciona pagamento.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "trialing",
                      "active",
                      "past_due",
                      "suspended",
                      "cancelled"
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Status atualizado"
          },
          "400": {
            "description": "ID ou status inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          },
          "401": {
            "description": "Administrador global requerido"
          },
          "404": {
            "description": "Assinatura não encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/platform/tenants": {
      "get": {
        "operationId": "listPlatformTenants",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista organizações para administradores da plataforma",
        "responses": {
          "200": {
            "description": "Organizações, até 500",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlatformTenant"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão sem papel de administrador"
          }
        }
      },
      "post": {
        "operationId": "createPlatformTenant",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Cria organização e associa o primeiro proprietário",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "owner_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "owner_display_name": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "owner_password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 8,
                    "maxLength": 72
                  },
                  "owner_email_verified": {
                    "type": "boolean",
                    "default": false,
                    "description": "Confirma manualmente o e-mail do proprietário após validação fora do e-mail; a decisão é registrada na auditoria."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending",
                      "active"
                    ],
                    "default": "active"
                  }
                },
                "required": [
                  "name",
                  "owner_email",
                  "owner_display_name",
                  "owner_password"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Organização e owner criados; por padrão o owner precisa confirmar o e-mail antes do login. Um administrador pode confirmar manualmente após validação fora do e-mail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tenant_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "owner_user_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "active"
                      ]
                    },
                    "email_verification_required": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "tenant_id",
                    "owner_user_id",
                    "status",
                    "email_verification_required"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "JSON inválido"
          },
          "401": {
            "description": "Sessão sem papel de administrador"
          },
          "409": {
            "description": "E-mail ou organização já existente"
          },
          "422": {
            "description": "Dados inválidos"
          }
        }
      }
    },
    "/api/v1/platform/tenants/{id}/status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "patch": {
        "operationId": "setPlatformTenantStatus",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Transiciona o ciclo de vida de uma organização",
        "description": "Requer platform_admin e X-CSRF-Token. Transições são validadas e auditadas. active e trialing permitem operação; pending, past_due, suspended, cancelled e deleted bloqueiam, revogam sessões persistentes e tokens/grants temporários do widget. deleted é soft-delete terminal e preserva dados.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending",
                      "trialing",
                      "active",
                      "past_due",
                      "suspended",
                      "cancelled",
                      "deleted"
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estado aplicado; conta quantas sessões foram revogadas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "trialing",
                        "active",
                        "past_due",
                        "suspended",
                        "cancelled",
                        "deleted"
                      ]
                    },
                    "changed": {
                      "type": "boolean"
                    },
                    "revoked_sessions": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "request_id": {
                      "type": "string",
                      "maxLength": 64
                    }
                  },
                  "required": [
                    "id",
                    "status",
                    "changed",
                    "revoked_sessions",
                    "request_id"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão sem papel de administrador"
          },
          "403": {
            "description": "Proteção CSRF falhou"
          },
          "404": {
            "description": "Organização não encontrada"
          },
          "422": {
            "description": "Status inválido ou transição fora do grafo permitido"
          },
          "500": {
            "description": "Não foi possível salvar a alteração ou sua auditoria"
          },
          "503": {
            "description": "Não foi possível obter o lock de ciclo de vida"
          },
          "409": {
            "description": "Transição de ciclo de vida não permitida; evento de auditoria foi gravado"
          }
        }
      }
    },
    "/api/v1/platform/users": {
      "get": {
        "operationId": "listPlatformUsers",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista contas para administradores da plataforma",
        "responses": {
          "200": {
            "description": "Contas, até 500",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlatformUser"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão sem papel de administrador"
          }
        }
      },
      "post": {
        "operationId": "createPlatformUser",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Cria conta e membership em tenant existente",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tenant_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "display_name": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 8,
                    "maxLength": 72
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "tenant_admin",
                      "supervisor",
                      "operator"
                    ]
                  }
                },
                "required": [
                  "tenant_id",
                  "email",
                  "display_name",
                  "password",
                  "role"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Conta e membership criadas; troca de senha obrigatória",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "tenant_id": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "required": [
                    "user_id",
                    "tenant_id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "JSON inválido"
          },
          "401": {
            "description": "Sessão sem papel de administrador"
          },
          "404": {
            "description": "Tenant ativo não encontrado"
          },
          "409": {
            "description": "E-mail existente ou membership conflitante"
          },
          "422": {
            "description": "Dados ou senha inválidos"
          }
        }
      }
    },
    "/api/v1/platform/users/{id}/status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "patch": {
        "operationId": "setPlatformUserStatus",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Suspende ou reativa uma conta sem excluí-la",
        "description": "Requer platform_admin e X-CSRF-Token. Suspender revoga todas as sessões persistentes da conta e registra auditoria append-only. A plataforma bloqueia a suspensão do último platform_admin ativo; reativar não restaura sessões antigas.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "suspended"
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estado aplicado; conta quantas sessões foram revogadas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "suspended"
                      ]
                    },
                    "changed": {
                      "type": "boolean"
                    },
                    "revoked_sessions": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "request_id": {
                      "type": "string",
                      "maxLength": 64
                    }
                  },
                  "required": [
                    "id",
                    "status",
                    "changed",
                    "revoked_sessions",
                    "request_id"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão sem papel de administrador"
          },
          "403": {
            "description": "Proteção CSRF falhou"
          },
          "404": {
            "description": "Conta não encontrada"
          },
          "409": {
            "description": "A última conta platform_admin ativa não pode ser suspensa"
          },
          "422": {
            "description": "Status diferente de active ou suspended"
          },
          "500": {
            "description": "Não foi possível salvar a alteração ou sua auditoria"
          },
          "503": {
            "description": "Não foi possível obter o lock de ciclo de vida"
          }
        }
      }
    },
    "/api/v1/public/plans": {
      "get": {
        "operationId": "listPublicPlans",
        "security": [],
        "summary": "Lista o catálogo público de planos ativos",
        "responses": {
          "200": {
            "description": "Planos ativos e política de preço fixo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BillingPlan"
                      }
                    },
                    "pricing_policy": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "data",
                    "pricing_policy"
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Catálogo indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        },
                        "request_id": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "code",
                        "message",
                        "request_id"
                      ]
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/recordings": {
      "get": {
        "operationId": "listRecordings",
        "summary": "Lista gravações do tenant autenticado",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "call_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "track",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "agent",
                "customer",
                "stereo",
                "video"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "processing",
                "ready",
                "partial",
                "failed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Metadados e paginação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecordingList"
                }
              }
            }
          },
          "400": {
            "description": "Filtro ou paginação inválidos"
          },
          "401": {
            "description": "Não autenticado"
          }
        }
      }
    },
    "/api/v1/recordings/{id}": {
      "get": {
        "operationId": "getRecording",
        "summary": "Obtém metadados de uma gravação do tenant autenticado",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Metadados da gravação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recording"
                }
              }
            }
          },
          "404": {
            "description": "Gravação não encontrada"
          }
        }
      }
    },
    "/api/v1/recordings/{id}/download": {
      "get": {
        "operationId": "downloadRecording",
        "summary": "Baixa WAV autenticado do tenant",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Arquivo WAV",
            "content": {
              "audio/wav": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Gravação não encontrada"
          }
        }
      }
    },
    "/api/v1/recordings/{id}/links": {
      "post": {
        "operationId": "renewRecordingLink",
        "summary": "Gera URL assinada de curta duração para uma faixa",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "URL HTTPS expira em até 15 minutos"
          },
          "404": {
            "description": "Gravação não encontrada"
          },
          "503": {
            "description": "Assinatura de links não configurada"
          }
        }
      }
    },
    "/api/v1/sessions": {
      "get": {
        "operationId": "listSessions",
        "summary": "Lista sessões do tenant autenticado",
        "responses": {
          "200": {
            "description": "Sessões",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionList"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createSession",
        "summary": "Cria e inicia uma sessão WhatsApp",
        "responses": {
          "201": {
            "description": "Sessão criada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "502": {
            "description": "Não foi possível iniciar a sessão WhatsApp"
          }
        }
      }
    },
    "/api/v1/sessions/{id}": {
      "get": {
        "operationId": "getSession",
        "summary": "Consulta status da sessão",
        "description": "Retorna somente ID e estado. QR e token de pareamento não fazem parte do escopo sessions:read.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Sessão"
          },
          "404": {
            "description": "Não encontrada"
          }
        }
      }
    },
    "/api/v1/sessions/{id}/connect": {
      "post": {
        "operationId": "connectSession",
        "summary": "Solicita conexão ou reconexão da sessão autorizada",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Sessão já conectada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "202": {
            "description": "Conexão iniciada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "404": {
            "description": "Sessão não encontrada no tenant"
          },
          "409": {
            "description": "Sessão saiu do WhatsApp e precisa ser pareada novamente"
          }
        }
      }
    },
    "/api/v1/sessions/{id}/disconnect": {
      "post": {
        "operationId": "disconnectSession",
        "summary": "Desconecta a sessão e desativa reconexão automática no restart",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfirmSessionDisconnect"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sessão desconectada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "400": {
            "description": "Confirmação explícita obrigatória"
          },
          "404": {
            "description": "Sessão não encontrada no tenant"
          }
        }
      }
    },
    "/api/v1/sessions/{id}/qr.png": {
      "get": {
        "operationId": "getSessionQr",
        "summary": "Obtém QR de pareamento temporário",
        "description": "Exige escopo sessions:write. O ID é validado dentro do tenant autenticado.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Imagem PNG",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "410": {
            "description": "QR expirado ou indisponível"
          }
        }
      }
    },
    "/api/v1/tenant": {
      "get": {
        "operationId": "getTenant",
        "summary": "Retorna o tenant associado à chave",
        "responses": {
          "200": {
            "description": "Tenant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tenant_id": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "required": [
                    "tenant_id"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/agent-assignments": {
      "get": {
        "operationId": "listTenantAgentAssignments",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista agentes atribuídos a membros do tenant",
        "description": "Requer sessão de pessoa com membership ativa tenant_owner ou tenant_admin. O tenant deriva da sessão; somente associações de memberships e agentes ativos são retornadas, até 5.000 atribuições.",
        "responses": {
          "200": {
            "description": "Atribuições ativas do tenant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TenantAgentAssignment"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão de tenant ausente ou expirada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Membership sem papel tenant_owner ou tenant_admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "500": {
            "description": "Não foi possível listar atribuições",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/agents": {
      "get": {
        "operationId": "listTenantAgents",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista agentes ativos do tenant selecionado",
        "description": "Requer sessão de pessoa com membership ativa tenant_owner ou tenant_admin. O tenant deriva da sessão autenticada; retorna até 500 agentes.",
        "responses": {
          "200": {
            "description": "Agentes ativos do tenant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TenantAgent"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão de tenant ausente ou expirada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Membership sem papel tenant_owner ou tenant_admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "500": {
            "description": "Não foi possível listar agentes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/audit": {
      "get": {
        "operationId": "listTenantAuditEvents",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista eventos recentes da organização autenticada",
        "description": "Requer sessão web de tenant_owner ou tenant_admin. O tenant é derivado do principal autenticado e não pode ser escolhido por parâmetro. Inclui eventos de tenant, faturamento e conta; metadata_json é deliberadamente omitido.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Quantidade máxima de eventos; padrão 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eventos do tenant autenticado ordenados do mais recente; data é lista vazia quando não há resultados.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer",
                            "format": "int64"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "tenant",
                              "billing",
                              "account"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "actor_id": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "resource_type": {
                            "type": "string"
                          },
                          "resource_id": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "source",
                          "created_at",
                          "actor_id",
                          "action",
                          "resource_type"
                        ]
                      }
                    },
                    "limit": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 200
                    }
                  },
                  "required": [
                    "data",
                    "limit"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Parâmetro limit fora do intervalo de 1–200",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente ou sem identidade de tenant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Papel sem permissão ou membership inativa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "Não foi possível ler os eventos de auditoria",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/data-export": {
      "get": {
        "operationId": "exportTenantData",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Baixa exportação JSON síncrona do tenant autenticado",
        "description": "Disponível somente para tenant_owner/tenant_admin com membership ativa no tenant ativo. O escopo deriva da sessão e não aceita tenant_id. A exportação síncrona inclui dados de membros/agentes, chamadas, eventos sem payload, metadados de gravações, integrações e uso; exclui senhas/hashes, segredos MFA, tokens, URLs/segredos de webhook, payloads e metadados de auditoria. Não inclui bytes de áudio. Limite: 10.000 linhas por coleção e 20 MiB; acima disso retorna 413. Para exportação assíncrona sem esses limites e com WAV local validado, use POST /api/v1/tenant/data-exports, consulte o job e faça download autenticado antes de expirar. Esta API não implementa exclusão de dados.",
        "responses": {
          "200": {
            "description": "Arquivo JSON com format_version, generated_at, tenant, data, included e excluded; resposta no-store. O uso é registrado na auditoria do tenant.",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "Nome de arquivo .json para download"
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store, private"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "format_version",
                    "generated_at",
                    "tenant",
                    "data",
                    "included",
                    "excluded"
                  ],
                  "properties": {
                    "format_version": {
                      "type": "integer",
                      "const": 1
                    },
                    "generated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "tenant": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "data": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      }
                    },
                    "included": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "excluded": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente ou sem identidade de tenant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Papel sem permissão ou membership inativa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "413": {
            "description": "Export excede limite síncrono; solicite o pacote assíncrono",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "Não foi possível criar exportação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/data-exports": {
      "post": {
        "operationId": "createTenantDataExport",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Solicita uma exportação assíncrona dos dados do tenant",
        "description": "Exige sessão web com membership ativa tenant_owner ou tenant_admin. Tenant é derivado da sessão. Uma chave Idempotency-Key armazena apenas o hash; reenvio pelo mesmo usuário e tenant retorna o mesmo job. Só há um job queued/running por tenant. O pacote ZIP contém export.json, metadados sem segredos e WAVs locais ready/partial que passam validação. Arquivos ausentes/inválidos falham o job. O artefato fica privado no host por 24 horas e é baixado por endpoint autenticado; não há URL assinada ou pública. Limite de conteúdo descompactado configurável, padrão 2 GiB. MariaDB usa uma transação de leitura, mas os WAVs são copiados durante a execução; banco e gravações não formam snapshot atômico.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Job queued ou já existente para a mesma chave; Location aponta para o estado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDataExportJobResponse"
                }
              }
            }
          },
          "400": {
            "description": "Idempotency-Key ausente ou inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão web de tenant ausente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Membership ou papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "409": {
            "description": "Já há uma exportação em andamento para este tenant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "Persistência de jobs indisponível ou não configurada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/data-exports/{id}": {
      "get": {
        "operationId": "getTenantDataExport",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Consulta estado de uma exportação",
        "description": "Revalida tenant_owner/tenant_admin ativo em cada requisição. IDs de outro tenant retornam 404. Quando ready, download_url é uma rota relativa que exige a mesma autenticação e membership.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Estado persistente da exportação",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDataExportJobResponse"
                }
              }
            }
          },
          "401": {
            "description": "Sessão web de tenant ausente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Membership ou papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Job ausente em outro tenant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/data-exports/{id}/download": {
      "get": {
        "operationId": "downloadTenantDataExport",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Baixa pacote ZIP privado da exportação pronta",
        "description": "Revalida tenant_owner/tenant_admin e tenant do job nesta requisição. O artefato local é servido diretamente com no-store; job expirado, não pronto, ausente ou de outro tenant não é baixável.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pacote ZIP contendo export.json e, quando disponíveis e validados, gravações WAV",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store, private"
                }
              }
            },
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Sessão web de tenant ausente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Membership ou papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Job não pronto, expirado, indisponível ou de outro tenant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/invitations": {
      "get": {
        "operationId": "listTenantInvitations",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista convites do tenant autenticado",
        "description": "Requer sessão web de tenant_owner ou tenant_admin no tenant autenticado. O tenant é derivado da identidade da sessão; tenant_id enviado pelo cliente não concede autoridade. Escritas também exigem validação CSRF.",
        "responses": {
          "200": {
            "description": "Convites, até 500",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TenantInvitation"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createTenantInvitation",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Cria convite de uso único",
        "description": "Requer sessão web de tenant_owner ou tenant_admin no tenant autenticado. O tenant é derivado da identidade da sessão; tenant_id enviado pelo cliente não concede autoridade. Escritas também exigem validação CSRF. O token aleatório fica somente como hash no banco e vai no e-mail por fragmento URL. Com entrega bem-sucedida, a API omite o token; sem SMTP ou após falha de envio, mostra invitation_token uma vez para compartilhamento manual. Validade padrão 72 horas, máximo 168 horas.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 255
                  },
                  "display_name": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "tenant_admin",
                      "supervisor",
                      "operator"
                    ]
                  },
                  "expires_in_hours": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 168
                  }
                },
                "required": [
                  "email",
                  "role"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Convite criado; token aparece somente se o e-mail não foi enviado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "email": {
                      "type": "string",
                      "format": "email"
                    },
                    "role": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "invitation_token": {
                      "type": "string"
                    },
                    "email_delivery": {
                      "type": "string",
                      "enum": [
                        "sent",
                        "failed",
                        "not_configured"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "email",
                    "role",
                    "expires_at",
                    "email_delivery"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "JSON inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "409": {
            "description": "Pessoa já é membro ativo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "402": {
            "description": "Assinatura ausente/inativa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de assentos atingido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "422": {
            "description": "Dados inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/invitations/accept": {
      "post": {
        "operationId": "acceptTenantInvitation",
        "security": [
          {},
          {
            "cookieAuth": []
          }
        ],
        "summary": "Aceita convite com token de uso único",
        "description": "Envie token exclusivamente no corpo JSON, nunca em query string nem path. /invite/ lê o fragmento URL e faz POST aqui. Token consumido uma vez. Para conta existente, autentique-se com o e-mail convidado. Para nova conta, display_name (se não veio no convite) e password são necessários.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 32,
                    "maxLength": 128
                  },
                  "display_name": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 8,
                    "maxLength": 72
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Convite aceito e membership ativada",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tenant_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "membership_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "role": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "const": "active"
                    }
                  },
                  "required": [
                    "tenant_id",
                    "membership_id",
                    "role",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Token ou JSON inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Entre na conta que recebeu o convite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem ou conta divergente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "410": {
            "description": "Token inválido, expirado, cancelado ou já usado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "402": {
            "description": "Assinatura ausente/inativa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de assentos atingido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "422": {
            "description": "Dados de nova conta inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/invitations/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "ID do convite no tenant autenticado."
        }
      ],
      "delete": {
        "operationId": "revokeTenantInvitation",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Cancela um convite pendente",
        "description": "Requer sessão web de tenant_owner ou tenant_admin no tenant autenticado. O tenant é derivado da identidade da sessão; tenant_id enviado pelo cliente não concede autoridade. Escritas também exigem validação CSRF.",
        "responses": {
          "204": {
            "description": "Convite cancelado"
          },
          "403": {
            "description": "Papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Convite não encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/members": {
      "get": {
        "operationId": "listTenantMembers",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista membros do tenant autenticado",
        "description": "Requer sessão web de tenant_owner ou tenant_admin no tenant autenticado. O tenant é derivado da identidade da sessão; tenant_id enviado pelo cliente não concede autoridade. Escritas também exigem validação CSRF.",
        "responses": {
          "200": {
            "description": "Membros, até 500",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TenantMember"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/members/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "ID da membership no tenant autenticado."
        }
      ],
      "patch": {
        "operationId": "updateTenantMember",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Altera papel ou status de um membro",
        "description": "Requer sessão web de tenant_owner ou tenant_admin no tenant autenticado. O tenant é derivado da identidade da sessão; tenant_id enviado pelo cliente não concede autoridade. Escritas também exigem validação CSRF. Papéis atribuíveis: tenant_admin, supervisor e operator. O último tenant_owner ativo não pode ser removido ou desativado.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "tenant_admin",
                      "supervisor",
                      "operator"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "inactive"
                    ]
                  }
                },
                "minProperties": 1,
                "maxProperties": 2
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Membership atualizada",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "user_id": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "user_id",
                    "role",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Atualização inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Membro não encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "409": {
            "description": "Último owner ativo não pode ser removido ou desativado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deactivateTenantMember",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Desativa a membership",
        "description": "Requer sessão web de tenant_owner ou tenant_admin no tenant autenticado. O tenant é derivado da identidade da sessão; tenant_id enviado pelo cliente não concede autoridade. Escritas também exigem validação CSRF. A operação pode ser revertida por PATCH com status active. O último tenant_owner ativo não pode ser desativado.",
        "responses": {
          "204": {
            "description": "Membership desativada"
          },
          "403": {
            "description": "Papel sem permissão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Membro não encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "409": {
            "description": "Último owner ativo não pode ser desativado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tenant/members/{id}/agents": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "ID da membership ativa no tenant selecionado.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "put": {
        "operationId": "replaceTenantMemberAgents",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Substitui a lista de agentes atribuídos a um membro",
        "description": "Requer sessão de pessoa com membership ativa tenant_owner ou tenant_admin e validação CSRF. O tenant deriva da sessão. A lista é substituída integralmente; envie [] para remover todas as atribuições. Cada agente deve ser ativo no mesmo tenant; limite de 500 IDs únicos.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceTenantMemberAgentsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lista integral de atribuições salva",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReplaceTenantMemberAgentsResult"
                }
              }
            }
          },
          "400": {
            "description": "JSON inválido ou mais de 500 IDs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão de tenant ausente ou expirada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "CSRF inválido ou membership sem papel tenant_owner/tenant_admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Membership inválida/inativa ou tenant indisponível"
          },
          "422": {
            "description": "ID inválido, duplicado ou agente fora do tenant/sem status ativo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "500": {
            "description": "Não foi possível salvar as atribuições",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "Lista destinos de webhook do tenant",
        "responses": {
          "200": {
            "description": "Destinos sem segredo"
          },
          "503": {
            "description": "Chave de criptografia não configurada"
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Cria destino HTTPS e revela o segredo uma única vez",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "HTTPS público; sem credenciais nem fragmento"
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "call.answered",
                        "call.connected",
                        "call.ended",
                        "call.failed",
                        "call.incoming",
                        "call.rejected",
                        "call.ringing",
                        "recording.failed",
                        "recording.processing",
                        "recording.ready",
                        "session.connected",
                        "session.disconnected",
                        "session.error",
                        "session.logged_out"
                      ]
                    },
                    "delivery_mode": {
                      "type": "string",
                      "enum": [
                        "json",
                        "multipart"
                      ],
                      "default": "json",
                      "description": "multipart só é usado para recording.ready; eventos de estado continuam JSON"
                    }
                  },
                  "delivery_mode": {
                    "type": "string",
                    "enum": [
                      "json",
                      "multipart"
                    ],
                    "default": "json",
                    "description": "multipart é aplicado somente a recording.ready; os eventos de estado permanecem JSON"
                  }
                },
                "description": "Destinos HTTPS; a configuração exige VOICE_WEBHOOK_ENCRYPTION_KEY."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Destino criado; signing_secret só aparece nesta resposta"
          },
          "400": {
            "description": "URL ou eventos inválidos"
          },
          "503": {
            "description": "Chave de criptografia não configurada"
          }
        }
      }
    },
    "/api/v1/webhooks/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "Lista entregas recentes do tenant",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entregas e estado de retry"
          },
          "503": {
            "description": "Chave de criptografia não configurada"
          }
        }
      }
    },
    "/api/v1/webhooks/deliveries/{id}/retry": {
      "post": {
        "operationId": "retryWebhookDelivery",
        "summary": "Reagenda entrega pendente ou em dead letter",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "202": {
            "description": "Reenvio agendado"
          },
          "404": {
            "description": "Entrega não encontrada ou não pode ser reenviada"
          },
          "503": {
            "description": "Chave de criptografia não configurada"
          }
        }
      }
    },
    "/api/v1/webhooks/{id}": {
      "delete": {
        "operationId": "disableWebhook",
        "summary": "Desativa destino do tenant",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "204": {
            "description": "Desativado"
          },
          "404": {
            "description": "Não encontrado"
          }
        }
      }
    },
    "/app/": {
      "get": {
        "operationId": "tenantAppPage",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Aplicação web do tenant autenticado",
        "description": "Sem sessão redireciona para /login/ (303); administradores globais são enviados para /admin/; conta sem tenant recebe 403.",
        "responses": {
          "200": {
            "description": "HTML da aplicação tenant",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "303": {
            "description": "Redirecionamento para /login/ ou /admin/"
          },
          "403": {
            "description": "Conta sem organização ativa"
          }
        }
      }
    },
    "/auth/email/verify": {
      "post": {
        "operationId": "verifyAccountEmail",
        "security": [],
        "summary": "Confirma e-mail com token de uso único",
        "description": "Recebe o token apenas no corpo JSON depois que /login/ removeu #verify_token= do endereço. O token expira em 24 horas, é consumido uma vez e somente seu hash fica no banco. A verificação revoga sessões antigas para exigir novo login.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 32,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Endereço confirmado; resposta não inclui token"
          },
          "400": {
            "description": "Token inválido, expirado ou já usado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem não permitida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido"
          },
          "503": {
            "description": "Verificação indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/email/verify/request": {
      "post": {
        "operationId": "requestAccountEmailVerification",
        "security": [],
        "summary": "Solicita confirmação de endereço sem revelar se a conta existe",
        "description": "Resposta 202 é neutra para endereço existente ou inexistente quando SMTP está configurado. Sem entrega SMTP, responde 503 sem emitir token. O link carrega o token apenas no fragmento #verify_token=; /login/ remove o fragmento antes de enviar o token no corpo JSON.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 255
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Pedido aceito com mensagem anti-enumeração"
          },
          "400": {
            "description": "Pedido inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem não permitida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido"
          },
          "503": {
            "description": "Verificação ou SMTP indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/login": {
      "post": {
        "operationId": "loginAccount",
        "security": [],
        "summary": "Autentica uma conta e cria sessão web protegida",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "format": "password"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sessão criada; resposta inclui papel, tenant, sinalizador de troca obrigatória e token CSRF; cookie HttpOnly"
          },
          "401": {
            "description": "Credenciais inválidas"
          },
          "403": {
            "description": "E-mail ainda não confirmado; nenhuma sessão é criada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido"
          }
        }
      }
    },
    "/auth/logout": {
      "post": {
        "operationId": "logoutAccount",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Encerra a sessão web com validação CSRF",
        "responses": {
          "204": {
            "description": "Sessão encerrada"
          }
        }
      }
    },
    "/auth/me": {
      "get": {
        "operationId": "getCurrentAccount",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Retorna a identidade e organização da sessão web atual",
        "responses": {
          "200": {
            "description": "Conta autenticada"
          },
          "401": {
            "description": "Sessão ausente ou expirada"
          }
        }
      }
    },
    "/auth/mfa": {
      "delete": {
        "operationId": "disableMFA",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Desabilita MFA da conta após reautenticação",
        "description": "MFA é opcional para todas as contas. Para desabilitá-lo, informe a senha atual, um TOTP ou código de recuperação ainda válido e valide o CSRF.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MFADisableInput"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "MFA desabilitado"
          },
          "400": {
            "description": "Senha e código MFA obrigatórios",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão, senha ou código MFA inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "CSRF inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "Falha ao desabilitar MFA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/mfa/challenge/enroll/confirm": {
      "post": {
        "operationId": "confirmMFALoginEnrollment",
        "security": [],
        "summary": "Confirma o TOTP do primeiro cadastro MFA e cria a sessão",
        "description": "Pré-autenticação: exige Origin da própria aplicação e X-CSRF-Token do desafio de cadastro. O desafio expira em cinco minutos. Em sucesso, ativa MFA, devolve os códigos de recuperação uma única vez e cria o cookie HttpOnly da sessão.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MFAChallengeCodeInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MFA ativado; códigos de recuperação exibidos uma única vez e sessão criada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MFAAccountSession"
                }
              }
            }
          },
          "400": {
            "description": "Código ou desafio malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Código ou desafio inválido/expirado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem ausente ou diferente da origem do serviço"
          },
          "409": {
            "description": "MFA já está habilitado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido"
          },
          "500": {
            "description": "Não foi possível criar a sessão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "Falha ao ativar MFA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/mfa/challenge/setup": {
      "post": {
        "operationId": "setupMFALoginChallenge",
        "security": [],
        "summary": "Obtém o segredo TOTP para configurar MFA durante o primeiro login",
        "description": "Pré-autenticação: requer Origin da própria aplicação e X-CSRF-Token emitido pelo desafio de login. O segredo e a URI otpauth são confidenciais e não devem ser registrados. A resposta não é armazenada em cache.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MFAChallengeInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Segredo TOTP e URI de provisionamento; expira em cinco minutos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MFAChallengeSetup"
                }
              }
            }
          },
          "400": {
            "description": "Desafio inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Desafio ausente, inválido ou expirado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem ausente ou diferente da origem do serviço"
          },
          "409": {
            "description": "A conta já possui MFA habilitado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido"
          },
          "500": {
            "description": "Não foi possível iniciar o provisionamento",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "MFA indisponível ou chave de criptografia não configurada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/mfa/challenge/verify": {
      "post": {
        "operationId": "verifyMFALoginChallenge",
        "security": [],
        "summary": "Verifica o MFA do login e cria uma sessão de conta",
        "description": "Pré-autenticação: exige Origin da própria aplicação e X-CSRF-Token do desafio. Aceita TOTP ou código de recuperação; o desafio é de uso limitado e expira em cinco minutos. Em sucesso, define o cookie HttpOnly da sessão e devolve o token CSRF.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MFAChallengeCodeInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sessão de conta criada após autenticação MFA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MFAAccountSession"
                }
              }
            }
          },
          "400": {
            "description": "Código ou desafio malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Código, desafio ou conta inválidos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem ausente ou diferente da origem do serviço"
          },
          "429": {
            "description": "Limite de tentativas excedido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "500": {
            "description": "Não foi possível criar a sessão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "MFA indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/mfa/enroll/confirm": {
      "post": {
        "operationId": "confirmMFAEnrollment",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Confirma o cadastro MFA da conta autenticada",
        "description": "O desafio pertence à conta autenticada e não pode ser reutilizado. Em sucesso, a sessão é rotacionada e os códigos de recuperação são devolvidos uma única vez.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MFAEnrollmentConfirmInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MFA ativado; guarde os códigos de recuperação, exibidos uma única vez",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MFAEnrollmentResult"
                }
              }
            }
          },
          "400": {
            "description": "Código ou desafio malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão, código ou desafio inválido/expirado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Validação CSRF falhou",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "409": {
            "description": "MFA já está habilitado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "Falha ao ativar MFA ou rotacionar a sessão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/mfa/enroll/start": {
      "post": {
        "operationId": "startMFAEnrollment",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Inicia o cadastro MFA para a conta autenticada",
        "description": "Exige senha atual e validação CSRF. O segredo TOTP é confidencial; a resposta não deve ser armazenada em cache ou registrada. Confirme o cadastro em até cinco minutos.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MFAEnrollmentStartInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Desafio, segredo TOTP e URI de provisionamento",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MFAEnrollmentStart"
                }
              }
            }
          },
          "400": {
            "description": "Senha atual ausente ou entrada inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente ou senha atual incorreta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Validação CSRF falhou",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "409": {
            "description": "MFA já está habilitado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "MFA ou chave de criptografia indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/mfa/status": {
      "get": {
        "operationId": "getMFAStatus",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Consulta o estado do MFA opcional da conta autenticada",
        "responses": {
          "200": {
            "description": "Estado MFA da conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MFAStatus"
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente ou expirada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "503": {
            "description": "MFA indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/organization": {
      "post": {
        "operationId": "selectAccountOrganization",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Troca o contexto da sessão persistente para uma membership ativa",
        "description": "Valida a membership e o tenant ativo no servidor, atualiza tenant_id na sessão persistente, registra auditoria e exige origem permitida e CSRF. A seleção é mantida após restart; a sessão de navegador em memória é descartada para carregar o novo contexto.",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tenant_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "tenant_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organização selecionada",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "name": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    },
                    "current": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "role",
                    "current"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Tenant inválido"
          },
          "401": {
            "description": "Sessão persistente ausente ou inválida"
          },
          "403": {
            "description": "CSRF/origem inválida ou conta platform_admin sem contexto de tenant"
          },
          "404": {
            "description": "A conta não possui membership ativa no tenant solicitado"
          },
          "503": {
            "description": "Não foi possível validar, auditar ou salvar a seleção"
          }
        }
      }
    },
    "/auth/organizations": {
      "get": {
        "operationId": "listAccountOrganizations",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista organizações ativas acessíveis pela conta",
        "responses": {
          "200": {
            "description": "Organizações acessíveis; administradores globais recebem uma lista vazia",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": "string"
                          },
                          "role": {
                            "type": "string"
                          },
                          "current": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "role",
                          "current"
                        ]
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente ou expirada"
          },
          "503": {
            "description": "Não foi possível listar organizações"
          }
        }
      }
    },
    "/auth/password/change": {
      "post": {
        "operationId": "changeAccountPassword",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Troca a senha da conta autenticada e libera a sessão inicial",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "current_password",
                  "new_password"
                ],
                "properties": {
                  "current_password": {
                    "type": "string",
                    "format": "password"
                  },
                  "new_password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 8,
                    "maxLength": 72
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Senha trocada"
          },
          "401": {
            "description": "Sessão ausente ou senha atual incorreta"
          },
          "403": {
            "description": "Validação CSRF falhou"
          },
          "422": {
            "description": "Nova senha fora dos limites"
          }
        }
      }
    },
    "/auth/password/forgot": {
      "post": {
        "operationId": "requestAccountPasswordReset",
        "security": [],
        "summary": "Solicita recuperação de senha sem revelar se a conta existe",
        "description": "Resposta 202 é neutra para endereço existente ou inexistente. Quando SMTP não está configurado, responde 503 sem emitir ou simular envio. O link de redefinição leva o token apenas no fragmento #reset_token=; a página remove o fragmento e envia o token no corpo JSON.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 255
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Pedido aceito com mensagem anti-enumeração"
          },
          "400": {
            "description": "Pedido inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem não permitida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Limite de tentativas excedido"
          },
          "503": {
            "description": "SMTP ou recuperação indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/password/reset": {
      "post": {
        "operationId": "resetAccountPassword",
        "security": [],
        "summary": "Redefine a senha com token de uso único",
        "description": "Recebe o token somente no corpo JSON, depois que /login/ o removeu do fragmento do endereço. Token expira em 30 minutos, é consumido uma vez e tem somente o hash armazenado.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "new_password"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 32,
                    "maxLength": 128
                  },
                  "new_password": {
                    "type": "string",
                    "format": "password",
                    "minLength": 8,
                    "maxLength": 72
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Senha alterada; sessões persistentes revogadas"
          },
          "400": {
            "description": "Token inválido, expirado ou já usado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Origem não permitida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "422": {
            "description": "Nova senha fora dos limites"
          },
          "429": {
            "description": "Limite de tentativas excedido"
          },
          "503": {
            "description": "Recuperação indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        }
      }
    },
    "/auth/sessions": {
      "get": {
        "operationId": "listAccountSessions",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Lista as sessões persistentes da conta na organização selecionada",
        "responses": {
          "200": {
            "description": "Sessões ativas no tenant atual; máximo de 100",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "user_agent": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_used_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "current": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "id",
                          "created_at",
                          "last_used_at",
                          "expires_at",
                          "current"
                        ]
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sessão ausente ou expirada"
          },
          "503": {
            "description": "Armazenamento de sessões indisponível"
          }
        }
      }
    },
    "/auth/sessions/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "delete": {
        "operationId": "revokeAccountSession",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "summary": "Revoga uma sessão persistente da própria conta e organização",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Sessão revogada; se era a atual, o cookie é removido"
          },
          "401": {
            "description": "Sessão ausente ou expirada"
          },
          "403": {
            "description": "Origem ou proteção CSRF inválida"
          },
          "404": {
            "description": "Sessão inexistente, expirada ou fora do escopo da conta"
          },
          "503": {
            "description": "Não foi possível revogar a sessão"
          }
        }
      }
    },
    "/dialer/embed": {
      "get": {
        "operationId": "loadDialerEmbed",
        "summary": "Carrega a interface de discagem incorporada",
        "security": [],
        "parameters": [
          {
            "name": "origin",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri-origin"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Documento HTML protegido por CSP frame-ancestors"
          },
          "403": {
            "description": "Origem não autorizada"
          }
        }
      }
    },
    "/dialer/embed/exchange": {
      "post": {
        "operationId": "exchangeDialerWidgetGrant",
        "summary": "Troca grant do widget por token temporário limitado à mesma sessão",
        "security": [],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri-origin",
              "description": "Deve corresponder à origem do serviço Voz."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "parent_origin": {
                    "type": "string",
                    "format": "uri-origin"
                  }
                },
                "required": [
                  "token",
                  "parent_origin"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Token de widget válido por 60 minutos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "session_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "access_token",
                    "session_id",
                    "expires_at"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Origem de chamada ausente ou inválida"
          },
          "410": {
            "description": "Grant expirado, consumido, revogado ou de outra origem"
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "operationId": "health",
        "security": [],
        "summary": "Verifica se o processo HTTP responde",
        "responses": {
          "200": {
            "description": "Processo ativo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/invite/": {
      "get": {
        "operationId": "tenantInvitationPage",
        "security": [],
        "summary": "Página pública para aceitar convite",
        "description": "A página lê o token do fragmento URL, remove-o do endereço e o envia no corpo JSON de POST /api/v1/tenant/invitations/accept. Nunca passe token em query string ou path.",
        "responses": {
          "200": {
            "description": "HTML público da página de convite",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/r/{id}": {
      "get": {
        "operationId": "downloadTemporaryRecording",
        "security": [],
        "summary": "Baixa faixa com capability HMAC temporária",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "name": "expires",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "sig",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Áudio WAV",
            "content": {
              "audio/wav": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Link inválido, expirado ou faixa indisponível"
          }
        }
      }
    },
    "/readyz": {
      "get": {
        "operationId": "ready",
        "summary": "Verifica banco de dados e readiness",
        "responses": {
          "200": {
            "description": "Pronto"
          },
          "404": {
            "description": "Token inválido"
          },
          "503": {
            "description": "Dependência indisponível"
          }
        }
      }
    },
    "/ws/calls/{id}/media": {
      "get": {
        "operationId": "connectCallMedia",
        "summary": "Abre WebSocket PCM S16LE full-duplex",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "name": "Sec-WebSocket-Protocol",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "voz.media.v1, voz-token.<token>"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "WebSocket estabelecido"
          },
          "401": {
            "description": "Token inválido, expirado ou já usado"
          }
        },
        "security": []
      }
    },
    "/ws/events": {
      "get": {
        "operationId": "subscribeEvents",
        "summary": "Assina eventos persistidos de chamada do tenant",
        "description": "O stream atual lê call_events. Use Bearer API key em clientes server-side ou o subprotocolo voz.events-token.{token} com token de uso único no browser. O cursor after é um BIGINT decimal em string; sem cursor, a conexão começa no evento mais recente.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Retomar após este ID de evento decimal; mantenha como string para evitar perda de precisão JavaScript.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]+$"
            }
          },
          {
            "name": "Sec-WebSocket-Protocol",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "voz.events.v1, voz.events-token.<token>"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "WebSocket established; event JSON text messages are ordered by cursor"
          },
          "401": {
            "description": "Credencial ausente, inválida ou expirada"
          },
          "403": {
            "description": "Escopo calls:read ausente"
          }
        }
      }
    },
    "/api/v1/tenant/notifications": {
      "get": {
        "tags": [
          "Tenant"
        ],
        "summary": "Listar notificações da organização",
        "description": "Requer sessão web de usuário com membership ativa na organização selecionada; tenant e destinatário vêm da sessão. Resposta sem cache e sem payloads dos eventos.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Notificações recentes e contagem não lida",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "unread_count",
                    "limit"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TenantNotification"
                      }
                    },
                    "unread_count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Sessão de tenant obrigatória"
          },
          "403": {
            "description": "Membership inativa ou organização indisponível"
          },
          "503": {
            "description": "Leitura indisponível"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/v1/tenant/notifications/unread-count": {
      "get": {
        "tags": [
          "Tenant"
        ],
        "summary": "Contar notificações não lidas",
        "description": "Requer sessão web com membership ativa. Conta apenas notificações do usuário autenticado na organização selecionada.",
        "responses": {
          "200": {
            "description": "Contagem não lida",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "unread_count"
                  ],
                  "properties": {
                    "unread_count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Sessão de tenant obrigatória"
          },
          "403": {
            "description": "Membership inativa"
          },
          "503": {
            "description": "Leitura indisponível"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/v1/tenant/notifications/{id}/read": {
      "post": {
        "tags": [
          "Tenant"
        ],
        "summary": "Marcar notificação como lida",
        "description": "Requer sessão web com membership ativa e proteção CSRF. Operação idempotente, limitada ao destinatário e à organização atual; IDs desconhecidos não são enumeráveis.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Atualizada ou já lida"
          },
          "401": {
            "description": "Sessão de tenant obrigatória"
          },
          "403": {
            "description": "Membership inativa"
          },
          "503": {
            "description": "Atualização indisponível"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "voz_operator"
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key"
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "schemas": {
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          }
        },
        "required": [
          "status"
        ]
      },
      "Session": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "status"
        ]
      },
      "SessionList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Session"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "CreateCall": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "to": {
            "type": "string",
            "pattern": "^\\+[1-9][0-9]{7,14}$",
            "description": "Destino E.164."
          },
          "destination": {
            "type": "string",
            "deprecated": true,
            "pattern": "^\\+[1-9][0-9]{7,14}$",
            "description": "Campo legado; prefira to."
          },
          "agent_id": {
            "type": "string",
            "format": "uuid",
            "description": "Agente atribuído à chamada, pertencente ao mesmo tenant."
          },
          "external_id": {
            "type": "string",
            "maxLength": 160,
            "description": "Identificador da entidade no CRM de origem."
          },
          "recording": {
            "type": "boolean",
            "description": "Habilita ou desabilita gravação para esta chamada."
          }
        },
        "required": [
          "session_id",
          "to"
        ]
      },
      "Call": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "call_id": {
            "type": "string",
            "format": "uuid"
          },
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ]
          },
          "peer": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "muted": {
            "type": "boolean",
            "description": "Estado do mudo local do microfone do operador"
          },
          "recording_enabled": {
            "type": "boolean"
          },
          "status_url": {
            "type": "string",
            "format": "uri-reference"
          },
          "events_url": {
            "type": "string",
            "format": "uri-reference",
            "description": "Endpoint WSS do tenant; exige token efêmero de eventos."
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "connected_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Momento em que a chamada foi conectada; usado para calcular a duração atendida."
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "id",
          "call_id",
          "session_id",
          "direction",
          "peer",
          "status",
          "muted",
          "started_at"
        ]
      },
      "CallList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Call"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "Recording": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "call_id": {
            "type": "string",
            "format": "uuid"
          },
          "track": {
            "type": "string",
            "enum": [
              "agent",
              "customer",
              "stereo",
              "video"
            ]
          },
          "format": {
            "type": "string",
            "const": "wav"
          },
          "channels": {
            "type": "integer"
          },
          "sample_rate": {
            "type": "integer"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "ready",
              "partial",
              "failed"
            ]
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "id",
          "call_id",
          "track",
          "format",
          "channels",
          "sample_rate",
          "size_bytes",
          "status",
          "started_at"
        ]
      },
      "RecordingList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Recording"
            }
          },
          "pagination": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "integer"
              },
              "offset": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              }
            },
            "required": [
              "limit",
              "offset",
              "total"
            ]
          }
        },
        "required": [
          "data",
          "pagination"
        ]
      },
      "TenantDataExportJobResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued",
                  "running",
                  "ready",
                  "failed",
                  "expired"
                ]
              },
              "error_code": {
                "type": "string"
              },
              "artifact_bytes": {
                "type": "integer",
                "format": "int64"
              },
              "recording_files": {
                "type": "integer",
                "format": "int64"
              },
              "row_count": {
                "type": "integer",
                "format": "int64"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "completed_at": {
                "type": "string",
                "format": "date-time"
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              },
              "download_url": {
                "type": "string",
                "format": "uri-reference"
              }
            },
            "required": [
              "id",
              "status",
              "created_at"
            ]
          },
          "idempotent_replay": {
            "type": "boolean"
          }
        },
        "required": [
          "data"
        ]
      },
      "APIError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "BillingError": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "error"
        ]
      },
      "TenantMember": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "display_name": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "user_id",
          "email",
          "display_name",
          "role",
          "status",
          "created_at",
          "updated_at"
        ]
      },
      "TenantInvitation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "display_name": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "accepted",
              "revoked",
              "expired"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "accepted_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "email",
          "role",
          "status",
          "expires_at",
          "created_at"
        ]
      },
      "APIKeyMetadata": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expired",
              "revoked"
            ]
          }
        },
        "required": [
          "id",
          "label",
          "scopes",
          "created_at",
          "expires_at",
          "revoked_at",
          "last_used_at",
          "status"
        ]
      },
      "BillingLimits": {
        "type": "object",
        "properties": {
          "users": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "sessions": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "concurrent_calls": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "call_minutes": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "storage_bytes": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "retention_days": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "webhooks": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "api_requests": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "users",
          "sessions",
          "concurrent_calls",
          "call_minutes",
          "storage_bytes",
          "retention_days",
          "webhooks",
          "api_requests"
        ],
        "additionalProperties": false
      },
      "BillingPlanVersion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer",
            "format": "uint32",
            "minimum": 1
          },
          "cycle": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "price_minor": {
            "type": "integer",
            "format": "uint64",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          },
          "limits": {
            "$ref": "#/components/schemas/BillingLimits"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "version",
          "cycle",
          "price_minor",
          "currency",
          "limits",
          "created_at"
        ]
      },
      "BillingPlan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "public": {
            "type": "boolean",
            "description": "Se falso, o plano só pode ser atribuído pela administração e não aparece no catálogo público nem inicia checkout."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "versions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingPlanVersion"
            }
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "active",
          "public",
          "created_at",
          "updated_at",
          "versions"
        ]
      },
      "BillingPlanList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingPlan"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "BillingPlanVersionInput": {
        "type": "object",
        "properties": {
          "cycle": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "price_minor": {
            "type": "integer",
            "format": "uint64",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          },
          "limits": {
            "$ref": "#/components/schemas/BillingLimits"
          }
        },
        "required": [
          "cycle",
          "currency",
          "limits"
        ]
      },
      "BillingPlanInput": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 2,
            "maxLength": 80,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "description": {
            "type": "string",
            "maxLength": 1000
          },
          "active": {
            "type": "boolean"
          },
          "public": {
            "type": "boolean",
            "description": "Se falso, o plano não aparece no catálogo público nem inicia checkout."
          },
          "versions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingPlanVersionInput"
            },
            "minItems": 1
          }
        },
        "required": [
          "slug",
          "name",
          "versions"
        ]
      },
      "BillingPlanUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "description": {
            "type": "string",
            "maxLength": 1000
          },
          "active": {
            "type": "boolean"
          },
          "public": {
            "type": "boolean"
          }
        },
        "minProperties": 1,
        "maxProperties": 4
      },
      "BillingSubscription": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "trialing",
              "active",
              "past_due",
              "suspended",
              "cancelled"
            ]
          },
          "current_period_start": {
            "type": "string",
            "format": "date-time"
          },
          "current_period_end": {
            "type": "string",
            "format": "date-time"
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "plan": {
            "$ref": "#/components/schemas/BillingPlan"
          },
          "plan_version": {
            "$ref": "#/components/schemas/BillingPlanVersion"
          }
        },
        "required": [
          "id",
          "tenant_id",
          "status",
          "current_period_start",
          "current_period_end",
          "cancel_at_period_end",
          "created_at",
          "plan",
          "plan_version"
        ]
      },
      "BillingMetricUsage": {
        "type": "object",
        "properties": {
          "metric": {
            "type": "string",
            "enum": [
              "users",
              "sessions",
              "concurrent_calls",
              "call_minutes",
              "storage_bytes",
              "retention_days",
              "webhooks",
              "api_requests"
            ]
          },
          "unit": {
            "type": "string",
            "enum": [
              "item",
              "minute",
              "byte",
              "day",
              "request"
            ]
          },
          "limit": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "used": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "remaining": {
            "anyOf": [
              {
                "type": "integer",
                "format": "int64"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "metric",
          "unit",
          "limit",
          "used",
          "remaining"
        ]
      },
      "BillingUsageEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "uint64"
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid"
          },
          "idempotency_key": {
            "type": "string"
          },
          "metric": {
            "type": "string",
            "enum": [
              "users",
              "sessions",
              "concurrent_calls",
              "call_minutes",
              "storage_bytes",
              "retention_days",
              "webhooks",
              "api_requests"
            ]
          },
          "quantity": {
            "type": "integer",
            "format": "int64"
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "source": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "tenant_id",
          "idempotency_key",
          "metric",
          "quantity",
          "period_start",
          "period_end",
          "source"
        ]
      },
      "BillingSummary": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string",
            "format": "uuid"
          },
          "subscription": {
            "$ref": "#/components/schemas/BillingSubscription"
          },
          "cycle": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "metrics": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingMetricUsage"
            }
          },
          "recent_events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingUsageEvent"
            }
          },
          "pricing_policy": {
            "type": "string"
          }
        },
        "required": [
          "tenant_id",
          "subscription",
          "cycle",
          "period_start",
          "period_end",
          "metrics",
          "recent_events",
          "pricing_policy"
        ]
      },
      "BillingEntitlements": {
        "type": "object",
        "properties": {
          "subscription_id": {
            "type": "string",
            "format": "uuid"
          },
          "plan": {
            "$ref": "#/components/schemas/BillingPlan"
          },
          "plan_version": {
            "$ref": "#/components/schemas/BillingPlanVersion"
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "metrics": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingMetricUsage"
            }
          }
        },
        "required": [
          "subscription_id",
          "plan",
          "plan_version",
          "period_start",
          "period_end",
          "metrics"
        ]
      },
      "PlatformTenant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "created_at"
        ]
      },
      "PlatformUser": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "display_name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "platform_role": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "email_verified": {
            "type": "boolean",
            "description": "Indica se a pessoa concluiu a confirmação do endereço de e-mail."
          },
          "mfa_enabled": {
            "type": "boolean",
            "description": "Indica somente se há um fator MFA ativo; nenhum segredo ou código é retornado."
          },
          "memberships": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "email",
          "display_name",
          "status",
          "platform_role",
          "created_at",
          "email_verified",
          "mfa_enabled",
          "memberships"
        ]
      },
      "PlatformSubscriptionPlanRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "format": "uint32",
            "minimum": 1
          },
          "cycle": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "price_minor": {
            "type": "integer",
            "format": "uint64",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "version",
          "cycle",
          "price_minor",
          "currency"
        ]
      },
      "PlatformSubscription": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid"
          },
          "plan_version_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "trialing",
              "active",
              "past_due",
              "suspended",
              "cancelled"
            ]
          },
          "current_period_start": {
            "type": "string",
            "format": "date-time"
          },
          "current_period_end": {
            "type": "string",
            "format": "date-time"
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "is_current": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "plan": {
            "$ref": "#/components/schemas/PlatformSubscriptionPlanRef"
          }
        },
        "required": [
          "id",
          "tenant_id",
          "plan_version_id",
          "status",
          "current_period_start",
          "current_period_end",
          "cancel_at_period_end",
          "is_current",
          "created_at",
          "plan"
        ]
      },
      "ConfirmSessionDisconnect": {
        "type": "object",
        "properties": {
          "confirm": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "confirm"
        ]
      },
      "BillingInvoice": {
        "type": "object",
        "required": [
          "provider_invoice_id",
          "subscription_id",
          "amount_minor",
          "currency",
          "invoice_status"
        ],
        "properties": {
          "provider_invoice_id": {
            "type": "string"
          },
          "provider_payment_id": {
            "type": "string"
          },
          "subscription_id": {
            "type": "string"
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "example": "BRL"
          },
          "invoice_status": {
            "type": "string"
          },
          "payment_status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "debit_at": {
            "type": "string",
            "format": "date-time"
          },
          "paid_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BillingInvoiceList": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingInvoice"
            }
          }
        }
      },
      "MFAChallengeInput": {
        "type": "object",
        "required": [
          "mfa_challenge"
        ],
        "properties": {
          "mfa_challenge": {
            "type": "string",
            "minLength": 1
          }
        }
      },
      "MFAChallengeCodeInput": {
        "type": "object",
        "required": [
          "mfa_challenge",
          "code"
        ],
        "properties": {
          "mfa_challenge": {
            "type": "string",
            "minLength": 1
          },
          "code": {
            "type": "string",
            "minLength": 6,
            "maxLength": 64,
            "description": "Código TOTP ou código de recuperação."
          }
        }
      },
      "MFAChallengeSetup": {
        "type": "object",
        "required": [
          "secret",
          "otpauth_url",
          "expires_in_seconds"
        ],
        "properties": {
          "secret": {
            "type": "string",
            "description": "Segredo TOTP confidencial."
          },
          "otpauth_url": {
            "type": "string",
            "format": "uri"
          },
          "expires_in_seconds": {
            "type": "integer",
            "const": 300
          }
        }
      },
      "MFAAccountSession": {
        "type": "object",
        "required": [
          "csrf_token",
          "platform_role",
          "tenant_id",
          "must_change_password",
          "recovery_codes"
        ],
        "properties": {
          "csrf_token": {
            "type": "string"
          },
          "platform_role": {
            "type": "string"
          },
          "tenant_id": {
            "type": "string",
            "description": "Contexto selecionado; vazio para platform_admin."
          },
          "must_change_password": {
            "type": "boolean"
          },
          "recovery_codes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Emitidos no cadastro MFA; null durante login MFA."
          }
        }
      },
      "MFAEnrollmentStartInput": {
        "type": "object",
        "required": [
          "current_password"
        ],
        "properties": {
          "current_password": {
            "type": "string",
            "format": "password",
            "minLength": 1,
            "maxLength": 72
          }
        }
      },
      "MFAEnrollmentStart": {
        "type": "object",
        "required": [
          "mfa_challenge",
          "csrf_token",
          "secret",
          "otpauth_url",
          "expires_in_seconds"
        ],
        "properties": {
          "mfa_challenge": {
            "type": "string"
          },
          "csrf_token": {
            "type": "string"
          },
          "secret": {
            "type": "string",
            "description": "Segredo TOTP confidencial."
          },
          "otpauth_url": {
            "type": "string",
            "format": "uri"
          },
          "expires_in_seconds": {
            "type": "integer",
            "const": 300
          }
        }
      },
      "MFAEnrollmentConfirmInput": {
        "type": "object",
        "required": [
          "mfa_challenge",
          "code"
        ],
        "properties": {
          "mfa_challenge": {
            "type": "string",
            "minLength": 1
          },
          "code": {
            "type": "string",
            "pattern": "^[0-9]{6}$"
          }
        }
      },
      "MFAEnrollmentResult": {
        "type": "object",
        "required": [
          "enabled",
          "recovery_codes",
          "csrf_token"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "const": true
          },
          "recovery_codes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "csrf_token": {
            "type": "string"
          }
        }
      },
      "MFAStatus": {
        "type": "object",
        "required": [
          "enabled",
          "required",
          "can_disable"
        ],
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "required": {
            "type": "boolean",
            "const": false,
            "description": "MFA não é obrigatório para nenhum papel."
          },
          "can_disable": {
            "type": "boolean"
          }
        }
      },
      "MFADisableInput": {
        "type": "object",
        "required": [
          "current_password",
          "code"
        ],
        "properties": {
          "current_password": {
            "type": "string",
            "format": "password",
            "minLength": 1,
            "maxLength": 72
          },
          "code": {
            "type": "string",
            "minLength": 6,
            "maxLength": 64,
            "description": "Código TOTP ou código de recuperação."
          }
        }
      },
      "TenantAgent": {
        "type": "object",
        "required": [
          "id",
          "email",
          "display_name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "display_name": {
            "type": "string"
          }
        }
      },
      "TenantAgentAssignment": {
        "type": "object",
        "required": [
          "membership_id",
          "agent_id"
        ],
        "properties": {
          "membership_id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ReplaceTenantMemberAgentsInput": {
        "type": "object",
        "required": [
          "agent_ids"
        ],
        "additionalProperties": false,
        "properties": {
          "agent_ids": {
            "type": "array",
            "maxItems": 500,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "ReplaceTenantMemberAgentsResult": {
        "type": "object",
        "required": [
          "membership_id",
          "agent_ids"
        ],
        "properties": {
          "membership_id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "TenantNotification": {
        "type": "object",
        "required": [
          "id",
          "category",
          "title",
          "message",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "category": {
            "type": "string",
            "enum": [
              "call",
              "recording",
              "session",
              "team"
            ]
          },
          "title": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "read_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
