{
  "openapi": "3.1.0",
  "info": {
    "title": "API do Lero",
    "version": "1.0.0",
    "summary": "O Lero transforma texto em fala em português do Brasil, com sotaque. A API do Lero recebe o texto e a voz e devolve um MP3, com as vozes rio (Rio de Janeiro), sp (São Paulo) e niteroi (Niterói). O Speech-to-Text transcreve áudio em português do Brasil, com o tempo de cada frase e legenda pronta. O Conversational Agent é um voice agent ao vivo por WebRTC, com o seu system prompt e knowledge base. O MCP do Lero coloca essa voz dentro do Claude Code, do Codex, do Cursor e do Claude Desktop.",
    "description": "Texto em fala (1 crédito por caractere) e transcrição de áudio (150 créditos por minuto de áudio) em português do Brasil. Docs: https://lero.studio/docs",
    "contact": {
      "name": "Lero",
      "email": "oi@lero.studio",
      "url": "https://lero.studio/docs"
    }
  },
  "servers": [
    {
      "url": "https://app.lero.studio/api/v1"
    }
  ],
  "security": [
    {
      "chave": []
    },
    {
      "chaveHeader": []
    }
  ],
  "paths": {
    "/tts": {
      "post": {
        "operationId": "criarFala",
        "summary": "Gerar fala",
        "description": "Transforma o texto em fala e devolve o MP3 (`audio/mpeg`). Custa 1 crédito por caractere do texto, cobrado antes de gerar e devolvido se a geração falhar.\n\nPrefere JSON? Mande `Accept: application/json` (sem `*/*`) e o áudio vem em base64 no campo `audio`.",
        "tags": [
          "Text-to-Speech (TTS)"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FalaPedido"
              },
              "example": {
                "texto": "Fala, tudo bem? Aqui é o Lero, direto do Rio.",
                "voz": "rio"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O MP3 da fala. Com `Accept: application/json` (sem `*/*`), o JSON com o áudio em base64.",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              },
              "X-Creditos-Custo": {
                "$ref": "#/components/headers/X-Creditos-Custo"
              },
              "X-Creditos-Restantes": {
                "$ref": "#/components/headers/X-Creditos-Restantes"
              },
              "X-Duracao": {
                "$ref": "#/components/headers/X-Duracao"
              }
            },
            "content": {
              "audio/mpeg": {
                "schema": {
                  "type": "string",
                  "contentMediaType": "audio/mpeg"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FalaJson"
                },
                "example": {
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b",
                  "custo": 45,
                  "saldo": 1499955,
                  "model": "Lero v1",
                  "voz": "rio",
                  "duracao": 3.12,
                  "formato": "mp3",
                  "audio": "SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjYx..."
                }
              }
            }
          },
          "400": {
            "description": "`texto_invalido`, `voz_invalida`, `modelo_invalido`, `pedido_invalido`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "texto_invalido",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "401": {
            "description": "`sem_chave`, `chave_invalida`, `chave_vencida`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_chave",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "402": {
            "description": "`sem_credito`, `teto_da_chave`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_credito",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "403": {
            "description": "`plano`, `escopo`, `bloqueado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "plano",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "429": {
            "description": "`muitos_pedidos`, `simultaneas`, `ocupado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "muitos_pedidos",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "500": {
            "description": "`interno`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "interno",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "502": {
            "description": "`falhou`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "falhou",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "503": {
            "description": "`carregando`, `motor_desligado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "carregando",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "504": {
            "description": "`demorou`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "demorou",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          }
        }
      }
    },
    "/stt": {
      "post": {
        "operationId": "transcrever",
        "summary": "Transcrever áudio",
        "description": "Transforma o áudio em texto, no português falado do Brasil: gírias, siglas, números e nomes no lugar. Devolve o texto com parágrafos e as frases com o tempo de início e fim; se você pedir, o tempo de cada palavra ou a legenda pronta (SRT ou VTT).\n\nMande o arquivo no campo `audio`, em `multipart/form-data` (até 4 MB), ou um link https público em `url` (até 500 MB; link assinado de S3, GCS ou R2 serve). Arquivo maior que 4 MB vai por `url`, num JSON com as mesmas opções. Formatos: MP3, WAV, M4A, OGG, OPUS, WEBM, FLAC e vídeo MP4 (o Lero usa o áudio do vídeo).\n\nCusta 150 créditos por minuto de áudio (2,5 por segundo), cobrado pelo segundo e arredondado para cima, só quando a transcrição dá certo: se falhar, nada é cobrado. Conta a duração do arquivo inteiro, silêncio incluído. Exemplo: 11 min de áudio custam 1.650 créditos.\n\nAté 2 h de áudio por pedido. A transcrição sai em cerca de 30 vezes o tempo real (1 h de áudio em uns 2 min): use timeout de 300 s. O Lero não guarda o áudio nem o texto da transcrição: apaga tudo ao responder.\n\n| `formato` | resposta |\n|---|---|\n| `json` (padrão) | JSON com o texto, as frases e o custo (ao lado) |\n| `texto` | `text/plain`: só o texto, com parágrafos |\n| `srt` | `application/x-subrip`: a legenda SRT |\n| `vtt` | `text/vtt`: a legenda WebVTT |\n\nNo JSON, `texto` traz parágrafos (`\\n\\n`) nas pausas longas e `segmentos` traz as frases, com `inicio` e `fim` em segundos. Com `timestamps=palavra`, cada frase traz também `palavras`: `[{inicio, fim, palavra}]`. O `formatado` vem `true` quando a revisão de pontuação e maiúsculas cobriu o texto todo. Os formatos `texto`, `srt` e `vtt` vêm em UTF-8.",
        "tags": [
          "Speech-to-Text (STT)"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TranscricaoArquivo"
              },
              "encoding": {
                "audio": {
                  "contentType": "audio/*, video/mp4, application/octet-stream"
                }
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranscricaoUrl"
              },
              "example": {
                "url": "https://exemplo.com/reuniao.mp3",
                "formato": "srt"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A transcrição. `formato=json` (padrão): o JSON. `texto`, `srt` e `vtt`: o arquivo direto, em UTF-8.",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              },
              "X-Creditos-Custo": {
                "$ref": "#/components/headers/X-Creditos-Custo"
              },
              "X-Creditos-Restantes": {
                "$ref": "#/components/headers/X-Creditos-Restantes"
              },
              "X-Duracao": {
                "$ref": "#/components/headers/X-Duracao"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transcricao"
                },
                "example": {
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b",
                  "custo": 1650,
                  "saldo": 1498350,
                  "model": "Lero v1",
                  "idioma": "pt-BR",
                  "duracao": 659.4,
                  "formatado": true,
                  "texto": "Bom dia, pessoal. Hoje a pauta é o lançamento da campanha de verão.\n\nPerfeito. Então...",
                  "segmentos": [
                    {
                      "inicio": 0.0,
                      "fim": 3.2,
                      "texto": "Bom dia, pessoal."
                    },
                    {
                      "inicio": 3.4,
                      "fim": 7.9,
                      "texto": "Hoje a pauta é o lançamento da campanha de verão."
                    }
                  ]
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                },
                "example": "Bom dia, pessoal. Hoje a pauta é o lançamento da campanha de verão.\n\nPerfeito. Então..."
              },
              "application/x-subrip": {
                "schema": {
                  "type": "string"
                },
                "example": "1\n00:00:00,000 --> 00:00:03,200\nBom dia, pessoal.\n\n2\n00:00:03,400 --> 00:00:07,900\nHoje a pauta é o lançamento da campanha de verão.\n"
              },
              "text/vtt": {
                "schema": {
                  "type": "string"
                },
                "example": "WEBVTT\n\n00:00:00.000 --> 00:00:03.200\nBom dia, pessoal.\n\n00:00:03.400 --> 00:00:07.900\nHoje a pauta é o lançamento da campanha de verão.\n"
              }
            }
          },
          "400": {
            "description": "`audio_invalido`, `url_invalida`, `formato_invalido`, `idioma_invalido`, `modelo_invalido`, `pedido_invalido`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "audio_invalido",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "401": {
            "description": "`sem_chave`, `chave_invalida`, `chave_vencida`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_chave",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "402": {
            "description": "`sem_credito`, `teto_da_chave`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_credito",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "403": {
            "description": "`plano`, `escopo`, `bloqueado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "plano",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "413": {
            "description": "`audio_grande`, `audio_longo`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "audio_grande",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "429": {
            "description": "`muitos_pedidos`, `simultaneas`, `ocupado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "muitos_pedidos",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "500": {
            "description": "`interno`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "interno",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "502": {
            "description": "`falhou`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "falhou",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "503": {
            "description": "`motor_desligado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "motor_desligado",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "504": {
            "description": "`demorou`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "demorou",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          }
        }
      }
    },
    "/voices": {
      "get": {
        "operationId": "listarVozes",
        "summary": "Listar vozes",
        "description": "A lista das vozes, com o id e o nome. Não gasta crédito.",
        "tags": [
          "Vozes"
        ],
        "responses": {
          "200": {
            "description": "As vozes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vozes"
                },
                "example": {
                  "vozes": [
                    {
                      "id": "rio",
                      "nome": "Rio de Janeiro"
                    },
                    {
                      "id": "sp",
                      "nome": "São Paulo"
                    },
                    {
                      "id": "niteroi",
                      "nome": "Niterói"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "`sem_chave`, `chave_invalida`, `chave_vencida`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_chave",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "403": {
            "description": "`plano`, `bloqueado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "plano",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          }
        }
      }
    },
    "/balance": {
      "get": {
        "operationId": "verSaldo",
        "summary": "Ver saldo",
        "description": "Os créditos da conta agora, já com a franquia do mês, e o plano. Não gasta crédito.",
        "tags": [
          "Conta"
        ],
        "responses": {
          "200": {
            "description": "O saldo e o plano.",
            "headers": {
              "X-Creditos-Restantes": {
                "$ref": "#/components/headers/X-Creditos-Restantes"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Saldo"
                },
                "example": {
                  "saldo": 1499955,
                  "plano": "Dev"
                }
              }
            }
          },
          "401": {
            "description": "`sem_chave`, `chave_invalida`, `chave_vencida`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_chave",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "403": {
            "description": "`plano`, `bloqueado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "plano",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          }
        }
      }
    },
    "/agents": {
      "get": {
        "operationId": "listarAgentes",
        "summary": "Listar versões e LLMs",
        "description": "O discovery do Conversational Agent: as versões do agente que a sua chave pode usar, os LLMs, as vozes e o setup recomendado. Chame antes de abrir a conversa: a lista muda. Não gasta crédito.",
        "tags": [
          "Conversational Agent"
        ],
        "responses": {
          "200": {
            "description": "As opções de agora.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agentes"
                },
                "example": {
                  "ligado": true,
                  "recomendado": {
                    "versao": "v0.4",
                    "llm": "oss120b",
                    "voz": "rio"
                  },
                  "versoes": [
                    {
                      "id": "v0.4",
                      "nome": "Agente v0.4",
                      "lingua": "pt",
                      "vozes": true
                    }
                  ],
                  "llms": [
                    {
                      "id": "oss120b",
                      "nome": "..."
                    }
                  ],
                  "vozes": [
                    "rio",
                    "sp",
                    "niteroi"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "`sem_chave`, `chave_invalida`, `chave_vencida`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_chave",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          }
        }
      }
    },
    "/agents/conversation": {
      "post": {
        "operationId": "abrirConversa",
        "summary": "Abrir conversa (WebRTC)",
        "description": "O signaling da conversa ao vivo: você manda a SDP offer do seu client WebRTC e recebe a SDP answer. Depois, o áudio e o data channel `chat` vão peer-to-peer entre o seu client e o agente (STT, LLM e TTS em streaming, com barge-in e turn detection). Sem `versao`, `llm` e `voz`, vale o recomendado. Com `instrucoes`, o agente vira o seu: o system prompt substitui a persona do Lero. Passo a passo em [Conversational Agent: passo a passo](#agente-guia).",
        "tags": [
          "Conversational Agent"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversaPedido"
              },
              "example": {
                "type": "offer",
                "sdp": "v=0\r\no=- ...",
                "versao": "v0.4",
                "llm": "oss120b",
                "voz": "rio"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A SDP answer: aplique com `setRemoteDescription`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversaResposta"
                },
                "example": {
                  "type": "answer",
                  "sdp": "v=0\r\no=- ..."
                }
              }
            }
          },
          "400": {
            "description": "`voz_invalida`, `pedido_invalido`, `oferta_invalida`, `versao_invalida`, `llm_invalido`, `roteiro_invalido`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "voz_invalida",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "401": {
            "description": "`sem_chave`, `chave_invalida`, `chave_vencida`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_chave",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "502": {
            "description": "`sem_bot`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "sem_bot",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          },
          "503": {
            "description": "`desligado`",
            "headers": {
              "X-Pedido-Id": {
                "$ref": "#/components/headers/X-Pedido-Id"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                },
                "example": {
                  "erro": "desligado",
                  "mensagem": "...",
                  "pedido": "3f9c1e0a7b2d4c6e8a1b3d5f7e9c0a2b"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "chave": {
        "type": "http",
        "scheme": "bearer",
        "description": "A chave `lero_...` no header Authorization."
      },
      "chaveHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Api-Key",
        "description": "A mesma chave, no header X-Api-Key."
      }
    },
    "headers": {
      "X-Pedido-Id": {
        "description": "O id do pedido. Mande junto se precisar de suporte.",
        "schema": {
          "type": "string"
        }
      },
      "X-Creditos-Custo": {
        "description": "Os créditos cobrados por este pedido.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Creditos-Restantes": {
        "description": "O saldo da conta depois do pedido.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Duracao": {
        "description": "A duração do áudio, em segundos.",
        "schema": {
          "type": "number"
        }
      },
      "Retry-After": {
        "description": "Quantos segundos esperar antes de tentar de novo.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "schemas": {
      "FalaPedido": {
        "type": "object",
        "required": [
          "texto",
          "voz"
        ],
        "properties": {
          "texto": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "O que o Lero vai falar, de 1 a 500 caracteres. Os espaços das pontas não contam."
          },
          "voz": {
            "type": "string",
            "enum": [
              "rio",
              "sp",
              "niteroi"
            ],
            "description": "A voz: `rio` (Rio de Janeiro), `sp` (São Paulo) ou `niteroi` (Niterói)."
          },
          "model": {
            "type": "string",
            "const": "Lero v1",
            "description": "`Lero v1`, o único de fala por enquanto."
          }
        }
      },
      "FalaJson": {
        "type": "object",
        "required": [
          "pedido",
          "custo",
          "model",
          "voz",
          "duracao",
          "formato",
          "audio"
        ],
        "properties": {
          "pedido": {
            "type": "string",
            "description": "O id do pedido."
          },
          "custo": {
            "type": "integer",
            "description": "Créditos cobrados."
          },
          "saldo": {
            "type": [
              "integer",
              "null"
            ],
            "description": "O saldo depois do pedido."
          },
          "model": {
            "type": "string",
            "const": "Lero v1"
          },
          "voz": {
            "type": "string",
            "enum": [
              "rio",
              "sp",
              "niteroi"
            ]
          },
          "duracao": {
            "type": "number",
            "description": "Segundos de áudio."
          },
          "formato": {
            "type": "string",
            "const": "mp3"
          },
          "audio": {
            "type": "string",
            "contentEncoding": "base64",
            "contentMediaType": "audio/mpeg"
          }
        }
      },
      "TranscricaoArquivo": {
        "type": "object",
        "oneOf": [
          {
            "required": [
              "audio"
            ]
          },
          {
            "required": [
              "url"
            ]
          }
        ],
        "properties": {
          "audio": {
            "type": "string",
            "format": "binary",
            "contentMediaType": "application/octet-stream",
            "description": "O arquivo de áudio, até 4 MB: MP3, WAV, M4A, OGG, OPUS, WEBM, FLAC ou vídeo MP4. Mande `audio` ou `url`, nunca os dois."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "description": "Link https público do áudio, até 500 MB; o Lero baixa. Para arquivo maior que 4 MB, use `url` (link assinado de S3, GCS, R2 etc. serve)."
          },
          "palavras_chave": {
            "type": "string",
            "description": "Até 50 nomes, marcas, siglas e termos para reconhecer e manter a grafia, ex. `[\"Lero\", \"Unflat\", \"CNPJ\"]`. No multipart, numa lista separada por vírgula: `Lero,Unflat,CNPJ`."
          },
          "formato": {
            "type": "string",
            "enum": [
              "json",
              "texto",
              "srt",
              "vtt"
            ],
            "default": "json",
            "description": "`json` (padrão), `texto` (text/plain), `srt` ou `vtt` (legendas prontas)."
          },
          "timestamps": {
            "type": "string",
            "enum": [
              "frase",
              "palavra"
            ],
            "default": "frase",
            "description": "`frase` (padrão) ou `palavra`: cada segmento ganha `palavras`, com o início e o fim de cada palavra. Só no `json`."
          },
          "formatar": {
            "type": "boolean",
            "default": true,
            "description": "A revisão de pontuação e maiúsculas, padrão `true`. Com `false`, volta mais rápido, com a pontuação do reconhecimento; o preço é o mesmo."
          },
          "idioma": {
            "type": "string",
            "enum": [
              "pt-BR"
            ],
            "default": "pt-BR",
            "description": "`pt-BR`, o único por enquanto."
          },
          "model": {
            "type": "string",
            "const": "Lero v1",
            "description": "`Lero v1`, o único de transcrição por enquanto."
          }
        }
      },
      "TranscricaoUrl": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "description": "Link https público do áudio, até 500 MB; o Lero baixa. Para arquivo maior que 4 MB, use `url` (link assinado de S3, GCS, R2 etc. serve)."
          },
          "palavras_chave": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "string"
            },
            "description": "Até 50 nomes, marcas, siglas e termos para reconhecer e manter a grafia, ex. `[\"Lero\", \"Unflat\", \"CNPJ\"]`. No multipart, numa lista separada por vírgula: `Lero,Unflat,CNPJ`."
          },
          "formato": {
            "type": "string",
            "enum": [
              "json",
              "texto",
              "srt",
              "vtt"
            ],
            "default": "json",
            "description": "`json` (padrão), `texto` (text/plain), `srt` ou `vtt` (legendas prontas)."
          },
          "timestamps": {
            "type": "string",
            "enum": [
              "frase",
              "palavra"
            ],
            "default": "frase",
            "description": "`frase` (padrão) ou `palavra`: cada segmento ganha `palavras`, com o início e o fim de cada palavra. Só no `json`."
          },
          "formatar": {
            "type": "boolean",
            "default": true,
            "description": "A revisão de pontuação e maiúsculas, padrão `true`. Com `false`, volta mais rápido, com a pontuação do reconhecimento; o preço é o mesmo."
          },
          "idioma": {
            "type": "string",
            "enum": [
              "pt-BR"
            ],
            "default": "pt-BR",
            "description": "`pt-BR`, o único por enquanto."
          },
          "model": {
            "type": "string",
            "const": "Lero v1",
            "description": "`Lero v1`, o único de transcrição por enquanto."
          }
        }
      },
      "Transcricao": {
        "type": "object",
        "required": [
          "pedido",
          "custo",
          "model",
          "idioma",
          "duracao",
          "formatado",
          "texto",
          "segmentos"
        ],
        "properties": {
          "pedido": {
            "type": "string",
            "description": "O id do pedido."
          },
          "custo": {
            "type": "integer",
            "description": "Créditos cobrados."
          },
          "saldo": {
            "type": [
              "integer",
              "null"
            ],
            "description": "O saldo depois do pedido."
          },
          "model": {
            "type": "string",
            "const": "Lero v1"
          },
          "idioma": {
            "type": "string",
            "const": "pt-BR"
          },
          "duracao": {
            "type": "number",
            "description": "Segundos de áudio, a base do custo."
          },
          "formatado": {
            "type": "boolean",
            "description": "`true` quando a revisão de pontuação e maiúsculas cobriu o texto todo."
          },
          "texto": {
            "type": "string",
            "description": "O texto inteiro, com parágrafos (`\\n\\n`) nas pausas longas."
          },
          "segmentos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Segmento"
            }
          }
        }
      },
      "Segmento": {
        "type": "object",
        "required": [
          "inicio",
          "fim",
          "texto"
        ],
        "properties": {
          "inicio": {
            "type": "number",
            "description": "Início, em segundos."
          },
          "fim": {
            "type": "number",
            "description": "Fim, em segundos."
          },
          "texto": {
            "type": "string",
            "description": "A frase."
          },
          "palavras": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Palavra"
            },
            "description": "Só com `timestamps=palavra`."
          }
        }
      },
      "Palavra": {
        "type": "object",
        "required": [
          "inicio",
          "fim",
          "palavra"
        ],
        "properties": {
          "inicio": {
            "type": "number"
          },
          "fim": {
            "type": "number"
          },
          "palavra": {
            "type": "string"
          }
        }
      },
      "Voz": {
        "type": "object",
        "required": [
          "id",
          "nome"
        ],
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "rio",
              "sp",
              "niteroi"
            ]
          },
          "nome": {
            "type": "string"
          }
        }
      },
      "Vozes": {
        "type": "object",
        "required": [
          "vozes"
        ],
        "properties": {
          "vozes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Voz"
            }
          }
        }
      },
      "Saldo": {
        "type": "object",
        "required": [
          "saldo",
          "plano"
        ],
        "properties": {
          "saldo": {
            "type": "integer",
            "description": "Créditos da conta agora."
          },
          "plano": {
            "type": "string",
            "enum": [
              "Grátis",
              "Starter",
              "Creator",
              "Dev"
            ]
          }
        }
      },
      "Agentes": {
        "type": "object",
        "required": [
          "ligado",
          "recomendado",
          "versoes",
          "llms",
          "vozes"
        ],
        "properties": {
          "ligado": {
            "type": "boolean"
          },
          "recomendado": {
            "type": "object",
            "properties": {
              "versao": {
                "type": "string"
              },
              "llm": {
                "type": "string"
              },
              "voz": {
                "type": "string"
              }
            }
          },
          "versoes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "nome",
                "lingua",
                "vozes"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "nome": {
                  "type": "string"
                },
                "lingua": {
                  "type": "string",
                  "enum": [
                    "pt",
                    "en"
                  ]
                },
                "vozes": {
                  "type": "boolean"
                }
              }
            }
          },
          "llms": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "nome"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "nome": {
                  "type": "string"
                }
              }
            }
          },
          "vozes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "rio",
                "sp",
                "niteroi"
              ]
            }
          }
        }
      },
      "ConversaPedido": {
        "type": "object",
        "required": [
          "type",
          "sdp"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "offer"
          },
          "sdp": {
            "type": "string",
            "maxLength": 20000,
            "description": "A SDP offer inteira, com o ICE completo (non-trickle), até 20 mil caracteres."
          },
          "versao": {
            "type": "string",
            "description": "A versão do agente, um id do `GET /v1/agents`. Recomendado: `v0.4`."
          },
          "llm": {
            "type": "string",
            "description": "O LLM, um id do `GET /v1/agents`. Recomendado: `oss120b`."
          },
          "voz": {
            "type": "string",
            "enum": [
              "rio",
              "sp",
              "niteroi"
            ],
            "description": "`rio`, `sp` ou `niteroi`. Ignorada na versão de voz única (`vozes: false`)."
          },
          "instrucoes": {
            "type": "string",
            "maxLength": 30000,
            "description": "System prompt, até 30 mil caracteres. Substitui a persona do Lero; as voice rules (nada de markdown, código ou URL na fala) vão junto."
          },
          "conhecimento": {
            "description": "Knowledge base (RAG): texto ou `[{titulo, texto}]`, até 50 itens e 60 mil caracteres. O agente consulta e não lê em voz alta.",
            "oneOf": [
              {
                "type": "string",
                "maxLength": 60000
              },
              {
                "type": "array",
                "maxItems": 50,
                "items": {
                  "type": "object",
                  "required": [
                    "texto"
                  ],
                  "properties": {
                    "titulo": {
                      "type": "string",
                      "maxLength": 200
                    },
                    "texto": {
                      "type": "string"
                    }
                  }
                }
              }
            ]
          },
          "abertura": {
            "type": "string",
            "maxLength": 2000,
            "description": "First message: como o agente abre a ligação. É uma instrução, não a frase exata."
          },
          "palavras": {
            "type": "array",
            "maxItems": 40,
            "items": {
              "type": "string",
              "maxLength": 40
            },
            "description": "STT keywords: até 40 nomes e termos para reconhecer."
          },
          "silencio_s": {
            "description": "Silence prompts. Desligado com `instrucoes`; `[20, 40, 60]` liga (2 toques e a despedida).",
            "oneOf": [
              {
                "type": "boolean",
                "const": false
              },
              {
                "type": "array",
                "maxItems": 3,
                "items": {
                  "type": "number",
                  "minimum": 2,
                  "maximum": 600
                }
              }
            ]
          },
          "pesquisa": {
            "type": "boolean",
            "description": "Web search tool. Desligada com `instrucoes`."
          },
          "max_tokens": {
            "type": "integer",
            "minimum": 60,
            "maximum": 1000,
            "description": "Tamanho máximo de cada resposta, de 60 a 1000 (padrão 260)."
          }
        }
      },
      "ConversaResposta": {
        "type": "object",
        "required": [
          "type",
          "sdp"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "answer"
          },
          "sdp": {
            "type": "string"
          }
        }
      },
      "Erro": {
        "type": "object",
        "required": [
          "erro",
          "mensagem"
        ],
        "properties": {
          "erro": {
            "type": "string",
            "enum": [
              "texto_invalido",
              "voz_invalida",
              "audio_invalido",
              "url_invalida",
              "formato_invalido",
              "idioma_invalido",
              "modelo_invalido",
              "pedido_invalido",
              "sem_chave",
              "chave_invalida",
              "chave_vencida",
              "sem_credito",
              "teto_da_chave",
              "plano",
              "escopo",
              "bloqueado",
              "audio_grande",
              "audio_longo",
              "muitos_pedidos",
              "simultaneas",
              "ocupado",
              "carregando",
              "motor_desligado",
              "falhou",
              "demorou",
              "interno",
              "oferta_invalida",
              "versao_invalida",
              "llm_invalido",
              "roteiro_invalido",
              "sem_bot",
              "desligado"
            ],
            "description": "Código fixo do erro."
          },
          "mensagem": {
            "type": "string",
            "description": "Texto para pessoas (pode mudar)."
          },
          "pedido": {
            "type": "string",
            "description": "O id do pedido (na fala e na transcrição)."
          },
          "campo": {
            "type": "string",
            "description": "O campo errado, nos erros 400."
          },
          "custo": {
            "type": "integer",
            "description": "No `sem_credito` da transcrição: os créditos que o áudio precisa."
          },
          "saldo": {
            "type": "integer",
            "description": "No `sem_credito` da transcrição: o saldo da conta."
          },
          "duracao": {
            "type": "number",
            "description": "No `sem_credito` e no `audio_longo` da transcrição: os segundos do áudio."
          },
          "cobrado": {
            "type": "integer",
            "description": "Créditos cobrados por um pedido que falhou (0 quando devolvidos)."
          }
        }
      }
    }
  }
}
