# Voz e mídia

> Vozes, troca de voz de um ou vários falantes, dublagem, importação de vídeo, legendas, cortes, ferramentas de áudio e publicação social via REST no Nodaro.

Source: https://nodaro.ai/pt-BR/docs/developers/api/voice-and-media

Os **endpoints de voz e mídia** expõem as ferramentas de voz e de mídia do Nodaro como REST simples. Eles cobrem catálogo de vozes, modificação de voz e troca de voz de vários falantes, dublagem, design de voz, importação de vídeo, legendas, cortes, ferramentas de áudio, transcrição e publicação em redes sociais. A maioria funciona como job: o `POST` retorna `{ jobId }` na hora, você consulta periodicamente `GET /v1/jobs/:id/status` até o status ser `completed` e lê o resultado em `output_data`.

As rotas funcionam em todas as edições, com as exceções indicadas abaixo, e recebem um bearer token. Veja [Autenticação](https://nodaro.ai/docs/developers/api/authentication) e, para consultar periodicamente vários jobs de uma vez, [Jobs](https://nodaro.ai/docs/developers/api/jobs#poll-many-jobs-at-once). Para enviar arquivos locais, faça o upload deles primeiro; veja [Envio de arquivos](https://nodaro.ai/docs/developers/api/uploads).

## Vozes
| Método | Caminho | O que faz |
| --- | --- | --- |
| `GET` | `/v1/voices` | O catálogo de vozes prontas: nome, `voice_id`, gênero, sotaque e idade. |
| `GET` | `/v1/voices/library` | Busca na Biblioteca de vozes compartilhada, com `search`, `gender`, `language`, `page`, `page_size` e mais. |
| `GET` | `/v1/voice-clones` | Os clones de voz que você fez antes de a clonagem ser descontinuada. |
| `PATCH` | `/v1/voice-clones/:id` | Renomeia ou edita um desses clones. |
| `DELETE` | `/v1/voice-clones/:id` | Exclui um desses clones. |
| `POST` | `/v1/voice-clones`, `/v1/voice-clones/from-url` | Descontinuadas. As duas retornam `410 voice_cloning_retired`. |

Sempre que uma rota aceita uma voz, passe o `voice_id` de uma voz do catálogo, o nome de uma voz pronta, como `Rachel`, ou o `elevenlabsVoiceId` de um clone. Os clones feitos antes da descontinuação continuam funcionando em todo lugar.

Um resultado da Biblioteca de vozes pode trazer `recommendedProvider`: o modelo de texto para fala em que a voz foi verificada. Quando o seu app não tem um seletor de modelo, envie esse valor como `provider` no [**Texto para fala** (Text to Speech)](https://nodaro.ai/docs/nodes/audio/text-to-speech), para que a voz soe como na prévia. `verifiedProviders` lista todos os modelos em que a voz foi verificada; quando o seu app tem um seletor, substitua a escolha do usuário só quando ela não estiver nessa lista. `hasMore`, na resposta, controla o “carregar mais”.

```ts

const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

const { voices, hasMore } = await client.voices.searchLibrary({ search: 'deep', language: 'en' })
const voice = voices[0]
await client.nodes.run('text-to-speech', {
text: 'Hello!',
voice: voice.voice_id,
voiceType: 'library',
...(voice.recommendedProvider ? { provider: voice.recommendedProvider } : {}),
})
```

## Criar uma voz ou falar com uma voz descrita
| Método | Caminho | Corpo | Resultado |
| --- | --- | --- | --- |
| `POST` | `/v1/voice-design` | `{ text, voiceDescription, model?, loudness?, guidanceScale?, seed?, quality?, shouldEnhance? }` | Uma prévia e um ID de voz reutilizável. |
| `POST` | `/v1/voice-remix` | `{ text, voiceDescription }` | Fala na voz descrita, sem clonagem. |

No [**Design de voz** (Voice Design)](https://nodaro.ai/docs/nodes/audio/voice-design), `text` é uma frase de prévia de 100 a 1.000 caracteres, `loudness` vai de -1 a 1 e `guidanceScale`, de 0 a 100. No [**Remix de voz** (Voice Remix)](https://nodaro.ai/docs/nodes/audio/voice-remix), `text` tem de 1 a 5.000 caracteres. Os dois retornam um job.

## Mudar a voz de uma gravação
`POST /v1/voice-changer` substitui a voz de uma faixa de áudio, ou de um vídeo inteiro com fala, por uma voz de destino. Envie exatamente um entre `audioUrl` e `videoUrl`; quando você envia os dois, o vídeo prevalece. Em um vídeo, o Nodaro extrai o áudio, muda a voz e coloca o áudio de volta na imagem original, e o `output_data` do job traz `videoUrl` e `audioUrl`.

**curl**

```bash
curl -X POST https://app.nodaro.ai/v1/voice-changer \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "videoUrl": "https://cdn.nodaro.ai/uploads/talking.mp4", "voiceId": "Aria", "removeBackgroundNoise": false }'
```

**TypeScript SDK**

```ts
const { jobId } = await client.voices.change({
videoUrl: 'https://cdn.nodaro.ai/uploads/talking.mp4',
voiceId: 'Aria',
})
```

**CLI**

```bash
nodaro voice changer --voice Aria --video https://cdn.nodaro.ai/uploads/talking.mp4 --watch
```

| Campo | O que faz |
| --- | --- |
| `voiceId` | Obrigatório. A voz de destino. |
| `audioUrl` ou `videoUrl` | Obrigatório, um dos dois. A gravação a modificar. |
| `model` | O modelo de fala para fala. |
| `stability`, `similarityBoost` | De 0 a 1. O quanto a interpretação é estável e o quanto a voz se aproxima da voz de destino. |
| `style` | De 0 a 1, padrão 0. Exagera a interpretação, com algum custo em velocidade e estabilidade. |
| `useSpeakerBoost` | Aumenta a semelhança com a voz de destino, um pouco mais devagar. |
| `seed` | Um inteiro que torna a saída repetível. |
| `removeBackgroundNoise` | `true` retorna só a voz limpa; `false` mantém a música e os efeitos sob a nova voz. |

Veja [**Modificador de voz** (Voice Changer)](https://nodaro.ai/docs/nodes/audio/voice-changer) para preços e dicas.

## Trocar a voz de vários falantes
O [**Modificador de voz Pro** (Voice Changer Pro)](https://nodaro.ai/docs/nodes/audio/voice-changer-pro) detecta cada falante de uma gravação e dá a cada um uma voz diferente, mantendo as palavras e o timing. Ele roda no Nodaro Cloud. Uma instalação self-hosted conectada ao Nodaro Cloud pode fazer a troca de voz em uma única chamada por meio dessa conexão; as etapas analyze e export exigem o próprio Nodaro Cloud. Veja [Conectar ao Nodaro Cloud](https://nodaro.ai/docs/self-hosting/cloud-connect).

| Método | Caminho | O que faz |
| --- | --- | --- |
| `POST` | `/v1/voice-changer-pro` | Troca a voz de cada falante mapeado, como um vídeo pronto ou como stems separados. |
| `POST` | `/v1/voice-changer-pro/analyze` | Detecta os falantes sem trocar as vozes. Só no Nodaro Cloud. |
| `POST` | `/v1/voice-changer-pro/export` | Renderiza um vídeo pronto a partir da sua própria mixagem dos stems. Só no Nodaro Cloud. |

### Mapear vozes para os falantes
`orderedVoices` mapeia os falantes pela ordem de detecção: o falante 0 recebe a entrada 0, o falante 1 recebe a entrada 1, e assim por diante, com 1 a 8 entradas. Os falantes que passam do fim da lista mantêm a própria voz. Cada entrada é uma destas:

- **Um ID de voz**, como `"Rachel"`.
- **`null`**, uma posição que mantém a voz original: esse falante mantém a própria voz, e os falantes seguintes ainda têm a voz trocada. Essas posições não custam nada, mas pelo menos uma entrada precisa ser uma voz.
- **Um objeto** com configurações por falante: `{ voiceId, engine?, stability?, similarityBoost?, style?, useSpeakerBoost?, seed?, volumeMode?, volume? }`. `engine` é `sts` (o padrão, fala para fala) ou `v3`, que interpreta a fala de novo a partir da transcrição com o ElevenLabs v3 e só aceita os valores 0; 0,5 e 1 em `stability`. `volumeMode` é `match` (o padrão), `normalize` ou `manual`, com `volume` de 0 a 200 por cento.

Antes de trocar as vozes, o Nodaro sempre separa as vozes da música e dos efeitos. O resto do corpo controla o resultado:

| Campo | O que faz |
| --- | --- |
| `audioUrl` ou `videoUrl` | Obrigatório, um dos dois. Em um vídeo, o áudio com as vozes trocadas volta para a imagem original. |
| `preserveBackground` | Mistura a música e os efeitos de volta sob as novas vozes. Padrão `true`. |
| `separationQuality` | `fast` (o padrão, preserva mais da voz) ou `best` (uma separação mais fina). |
| `removeBackgroundNoise` | Também remove o ruído do resultado. |
| `musicVolumeMode`, `musicVolume` | O nível do fundo mantido: `match` (o padrão), `normalize` ou `manual`, com 0 a 200 por cento. |
| `voiceFx` | Uma reverberação ou um eco só nas vozes: `{ preset, wetDryMix?, delayMs?, decay? }`. As predefinições de reverberação, como `room`, `hall` ou `church`, usam `wetDryMix`; `echo` e `custom` usam `delayMs` e `decay`. |
| `output` | `video` (o padrão, o resultado pronto) ou `stems` (faixas secas, sem nivelamento, para a sua própria mixagem). |
| `analysis` | O `output_data` de um job analyze anterior. A troca de voz reutiliza os falantes e os stems dessa análise, em vez de detectá-los de novo. |

A troca de voz é cobrada por falante mapeado; analyze e export têm preços fixos. Veja os preços em [Modificador de voz Pro](https://nodaro.ai/docs/nodes/audio/voice-changer-pro).

### O fluxo interativo: analyze, troca de voz em stems, export
A troca de voz em uma única chamada renderiza um vídeo pronto de uma vez. O fluxo interativo permite verificar os falantes antes de pagar pela troca de voz e mixar o resultado antes de renderizá-lo.

### Detectar os falantes
`POST /v1/voice-changer-pro/analyze` com `audioUrl` ou `videoUrl` e, opcionalmente, `separationQuality` e `suggestTitle`. O `output_data` do job concluído traz `vocalsUrl` e `backgroundUrl`, já separados, e `languageCode` e `languageProbability`, detectados. A lista `speakers` dá, para cada falante, `id`, `segments`, `firstStartSec`, `wordCount` e um `snippet`. Ele também traz um `suggestedTitle` quando você pede um. Procure “falantes” que não são pessoas, como aplausos.

### Trocar as vozes em stems
`POST /v1/voice-changer-pro` com `output: "stems"` e todo o `output_data` da análise como `analysis`. A troca de voz pula a detecção, então você pode trocar as vozes de novo, com outras vozes, sem pagar pela detecção outra vez.

### Mixar e exportar
Defina o nível de cada stem na sua própria interface; isso não custa nada. Depois, envie `POST /v1/voice-changer-pro/export` com o `videoUrl` de origem e até 16 `tracks`, cada uma `{ url, gain, muted, kind? }`. `gain` vai de 0 a 200, `kind` é `voice` ou `background`, e pelo menos uma faixa precisa estar com som. Um `voiceFx` opcional se aplica só às faixas de voz. O fluxo de vídeo é copiado, nunca codificado de novo, então a exportação corresponde à sua prévia. O `output_data.videoUrl` do job concluído é o resultado.

**curl**

```bash
AUTH="Authorization: Bearer $NODARO_API_KEY"
VIDEO="https://cdn.nodaro.ai/uploads/panel.mp4"

# 1. Detect the speakers
JOB=$(curl -s -X POST https://app.nodaro.ai/v1/voice-changer-pro/analyze -H "$AUTH" \
  -H 'Content-Type: application/json' -d "{\"videoUrl\":\"$VIDEO\",\"suggestTitle\":true}" | jq -r .jobId)
# ...poll until completed, then keep the analysis
ANALYSIS=$(curl -s "https://app.nodaro.ai/v1/jobs/$JOB/status" -H "$AUTH" | jq .data.output_data)

# 2. Recast to stems: speaker 2 keeps their own voice
JOB=$(curl -s -X POST https://app.nodaro.ai/v1/voice-changer-pro -H "$AUTH" -H 'Content-Type: application/json' \
  -d "{\"videoUrl\":\"$VIDEO\",\"orderedVoices\":[\"Rachel\",null,\"Aria\"],\"output\":\"stems\",\"analysis\":$ANALYSIS}" | jq -r .jobId)

# 3. Export your mix
curl -s -X POST https://app.nodaro.ai/v1/voice-changer-pro/export -H "$AUTH" -H 'Content-Type: application/json' -d "{
\"videoUrl\": \"$VIDEO\",
\"tracks\": [
{ \"url\": \"<voice stem 0>\", \"gain\": 100, \"muted\": false },
{ \"url\": \"<voice stem 1>\", \"gain\": 90, \"muted\": false },
{ \"url\": \"<background stem>\", \"gain\": 70, \"muted\": false, \"kind\": \"background\" }
],
\"voiceFx\": { \"preset\": \"hall\", \"wetDryMix\": 25 }
}"
```

**TypeScript SDK**

```ts
const { jobId: analyzeJob } = await client.voices.analyze({ videoUrl, suggestTitle: true })
const analysis = (await client.jobs.get(analyzeJob)).output_data // once completed

const { jobId: recastJob } = await client.voices.recast({
videoUrl,
orderedVoices: ['Rachel', null, 'Aria'],
output: 'stems',
analysis,
})
const stems = (await client.jobs.get(recastJob)).output_data // once completed

await client.voices.exportMix({
videoUrl,
tracks: [
{ url: stems.tracks[0].url, gain: 100, muted: false },
{ url: stems.tracks[1].url, gain: 90, muted: false },
{ url: stems.backgroundUrl, gain: 70, muted: false, kind: 'background' },
],
voiceFx: { preset: 'hall', wetDryMix: 25 },
})
```

**CLI**

```bash
nodaro voice analyze --video https://cdn.nodaro.ai/uploads/panel.mp4 --watch --json > analyze.json
jq .output_data analyze.json > analysis.json

nodaro voice recast --video https://cdn.nodaro.ai/uploads/panel.mp4 --voices Rachel,keep,Aria \
  --analysis-file analysis.json --output stems --watch

nodaro voice export --source https://cdn.nodaro.ai/uploads/panel.mp4 --tracks-file mix.json \
  --voice-fx hall --voice-fx-mix 25 --watch
```

Na CLI, a palavra `keep` em `--voices` cria uma posição que mantém a voz original.

## Dublar para outro idioma
`POST /v1/dubbing` traduz e dubla uma faixa de áudio ou um vídeo inteiro, mantendo a voz de cada falante. Envie exatamente uma origem: `audioUrl`, `videoUrl` ou `sourceUrl` (um link público, como uma página do YouTube ou do TikTok, que o próprio modelo de dublagem busca). Um vídeo volta como vídeo dublado: `output_data.videoUrl`, mais a faixa dublada sozinha em `output_data.audioUrl`.

| Campo | O que faz |
| --- | --- |
| `targetLanguage` | Obrigatório. Um código ISO, por exemplo `es` ou `pt-BR`. |
| `sourceLanguage` | O idioma falado. É detectado quando você o omite. |
| `numSpeakers` | `0` (o padrão) detecta os falantes; de 1 a 20 melhora a separação quando você sabe o número. |
| `startTime`, `endTime` | Dubla só essa janela da origem, em segundos. |
| `disableVoiceCloning`, `dropBackgroundAudio` | Não reproduz a voz própria de cada falante, ou descarta a música e os efeitos. |
| `highestResolution` | Mantém a resolução de origem em um vídeo. |
| `useProfanityFilter`, `targetAccent`, `watermark` | Um filtro de palavrões, um sotaque para o idioma de destino e a marca-d’água do modelo de dublagem em um vídeo. |

Uma dublagem é cobrada por minuto do trecho dublado, com mínimo de 1 minuto. O trecho pode ter até 30 minutos; um trecho mais longo retorna `413`, então duble uma janela com `startTime` e `endTime`. Veja [**Dublagem** (Dubbing)](https://nodaro.ai/docs/nodes/audio/dubbing).

## Importar um vídeo de um link
`POST /v1/download-video` importa para o seu armazenamento um vídeo de rede social (YouTube, TikTok, Instagram, X ou Facebook) ou um link direto para um arquivo de vídeo. A rota retorna `{ downloadId }`, não um ID de job. O arquivo pronto vai para a sua biblioteca.

| Campo | O que faz |
| --- | --- |
| `url` | Obrigatório. A página ou o arquivo a importar. |
| `maxHeight` | Limita a resolução, por exemplo `720`. Omita para obter a melhor disponível. |
| `sectionStartSec`, `sectionEndSec` | Importa só esse intervalo, em segundos, com cerca de 3 segundos a mais em cada ponta, porque o corte cai nos quadros-chave. Envie os dois ou nenhum, e corte o resultado para ter um corte exato. |
| `requireAudio` | Por padrão, um resultado sem som falha, porque isso geralmente indica uma origem degradada. Envie `false` para aceitar um clipe sem som. |

Acompanhe o download com `GET /v1/download-video/progress/:downloadId`, um fluxo de server-sent events. Mais ou menos a cada 500 ms, ele envia `{ phase, percent, videoUrl?, thumbnailUrl?, error? }`, e termina em `completed`, com o `videoUrl` armazenado, ou em `failed`, com o `error`. Comece a ouvir assim que a importação começar, porque o progresso só fica guardado por pouco tempo depois que o download termina. No máximo 4 downloads ficam em andamento ao mesmo tempo por conta; um quinto retorna `429 too_many_downloads`.

**curl**

```bash
DL=$(curl -s -X POST https://app.nodaro.ai/v1/download-video \
  -H "Authorization: Bearer $NODARO_API_KEY" -H 'Content-Type: application/json' \
  -d '{ "url": "https://www.youtube.com/watch?v=VIDEO_ID", "maxHeight": 720 }' | jq -r .downloadId)

curl -sN https://app.nodaro.ai/v1/download-video/progress/$DL \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts
const { downloadId } = await client.media.downloadVideo({
url: 'https://www.youtube.com/watch?v=VIDEO_ID',
maxHeight: 720,
})
for await (const event of client.media.downloadVideoProgress(downloadId)) {
console.log(`${event.phase} ${event.percent}%`)
if (event.phase === 'completed') console.log('stored at', event.videoUrl)
}
```

**CLI**

```bash
nodaro media download "https://www.youtube.com/watch?v=VIDEO_ID" --max-height 720 --watch
nodaro media metadata "https://www.youtube.com/watch?v=VIDEO_ID"
```

Duas rotas relacionadas respondem na hora:

- `POST /v1/video-metadata` com `{ url }` lê a duração, o tamanho, o título e o status de transmissão ao vivo de um vídeo sem baixá-lo. Use essa rota para decidir se vale importar só um trecho.
- `POST /v1/save-to-storage` com `{ mediaUrl, filename?, mediaType? }` copia qualquer URL de mídia externa para o seu armazenamento, no servidor. A rota retorna um job.

## Editar vídeo
| Método | Caminho | O que faz | Preço |
| --- | --- | --- | --- |
| `POST` | `/v1/trim-video` | Corta um vídeo em um intervalo. | [**Cortar vídeo** (Trim Video)](https://nodaro.ai/docs/nodes/video/trim-video) |
| `POST` | `/v1/add-captions` | Embute legendas em um vídeo. | [**Adicionar legendas** (Add Captions)](https://nodaro.ai/docs/nodes/video/add-captions) |
| `POST` | `/v1/still-to-video` | Uma imagem e uma faixa de áudio viram um MP4. | 0 créditos |
| `POST` | `/v1/slideshow` | De 2 a 100 imagens e uma faixa de áudio opcional viram um MP4. | 0 créditos |
| `POST` | `/v1/video-overlay` | Coloca de 1 a 20 camadas de imagem com tempo definido sobre um vídeo. | 22 créditos |
| `POST` | `/v1/media/process` | Corta ou recorta um arquivo armazenado e responde na hora. | Grátis |

### Cortar um vídeo
`POST /v1/trim-video` recebe `videoUrl` e o intervalo na unidade que você preferir: `startTime` e `endTime` em segundos, `trimStartFrames` e `trimEndFrames`, `trimStartSeconds` e `trimEndSeconds`, ou `keepFirstSeconds` ou `keepLastSeconds`.

### Embutir legendas
`POST /v1/add-captions` recebe `videoUrl` e as palavras da primeira fonte que encontra, nesta ordem: `captions` (entradas com tempo por palavra), `transcript`, `text` no estilo `subtitle` e, por fim, a transcrição automática da fala.

| Campo | O que faz |
| --- | --- |
| `style` | `subtitle` (um bloco estático) ou um estilo cinético: `word-highlight`, `karaoke`, `tiktok-words`, `word-pop` ou `bouncy`. |
| `text` | Em `subtitle`: a própria legenda, embutida como um bloco estático durante todo o vídeo. Em um estilo cinético: só o texto de reserva, usado quando a transcrição não encontra nada. |
| `captions` | Entradas com tempo por palavra, `{ text, startMs, endMs }`, uma por palavra nos estilos cinéticos. As `words` de uma transcrição servem como estão. |
| `auto_transcribe`, `transcribe_provider` | Transcreve a fala quando nada mais fornece as palavras. Um estilo cinético precisa de marcações de tempo por palavra: `incredibly-fast-whisper` (o padrão) ou `elevenlabs-stt`. |
| `look` | `outline` (o padrão nos estilos cinéticos) ou `clean` (o padrão em `subtitle`). |
| `maxWordsPerLine` | De 1 a 20 palavras por linha, além do limite imposto pela largura do quadro. Com 1 ou 2, a leitura fica mais impactante. |
| `highlightColor`, `animate` | Só nos estilos cinéticos; `400` em `subtitle`. |
| `position`, `positionY`, `fontSize`, `fontFamily`, `fontWeight`, `color`, `backgroundColor`, `strokeColor`, `strokeWidth`, `uppercase` | A aparência do texto. |
| `segments` | Dá a trechos do vídeo um estilo, um visual e uma posição próprios. |

Uma `subtitle` de texto simples, sem estilização, é a renderização barata. Um estilo cinético, qualquer campo de estilização, legendas com tempo, a transcrição automática ou `segments` renderizam pelo preço mais alto e mantêm a taxa de quadros da origem. Veja todas as opções e os dois preços em [Adicionar legendas](https://nodaro.ai/docs/nodes/video/add-captions).

### Transformar imagens fixas em vídeo
- **`POST /v1/still-to-video`** recebe `{ imageUrl, audioUrl, motion?, intensity?, resolution?, aspectRatio?, fps?, fit?, padColor? }`. O vídeo dura o mesmo que o áudio; não existe campo de duração. `motion` é `none` (o padrão), `zoom-in`, `zoom-out`, `pan-left`, `pan-right` ou `ken-burns`, com `intensity` de 1 a 10. Veja [**Imagem fixa para vídeo** (Still to Video)](https://nodaro.ai/docs/nodes/video/still-to-video).
- **`POST /v1/slideshow`** recebe `imageUrls` (de 2 a 100), um `audioUrl` opcional, `imageDurations` (segundos por imagem, `null` para automático) ou `perImageDuration`, uma `transition` e os mesmos campos de aparência. Com áudio, a apresentação dura o mesmo que o áudio; as durações fixadas que não somam esse total são ajustadas proporcionalmente, e a saída do job avisa isso. Sem áudio, ela dura o número de imagens vezes `perImageDuration`, em silêncio. Veja [**Apresentação de slides** (Slideshow)](https://nodaro.ai/docs/nodes/video/slideshow).

As duas rotas renderizam no servidor, sem modelo de IA, e custam 0 créditos. `resolution` é `720p`, `1080p` ou `4K`, `fps` é `24` ou `30`, e `fit: "contain"` adiciona faixas com `padColor` em vez de cortar a imagem.

### Sobrepor imagens a um vídeo
`POST /v1/video-overlay` coloca de 1 a 20 camadas de imagem sobre um vídeo em uma única renderização e mantém o áudio original intacto. Cada camada é `{ imageUrl, start, end?, preset?, corner?, anchor?, x?, y?, width?, height?, fit?, opacity?, animate?, zIndex? }`. `start` e `end` são segundos; sem `end`, a camada dura até o fim do vídeo. `preset` é `card`, `corner-badge` ou `full-frame`, e uma posição ou um tamanho explícito o substitui. Uma camada sem nenhum dos dois é um selo de canto, no canto inferior direito, a menos que `corner` indique outro.

`outputAspect` (`16:9`, `9:16`, `1:1` ou `4:5`) muda o quadro, com `baseFit` e `backgroundColor`. O job retorna `{ videoUrl, thumbnailUrl, width, height, durationSec, warnings }`. A rota custa 22 créditos por execução, qualquer que seja o número de camadas, e cada usuário pode enviar 30 requisições por minuto. Veja [**Sobreposição em vídeo** (Video Overlay)](https://nodaro.ai/docs/nodes/video/video-overlay).

### Cortar ou recortar um arquivo armazenado na hora
`POST /v1/media/process` corta ou recorta um arquivo que você armazenou e responde na mesma requisição, de graça. O corpo é `{ sourceUrl, type, trim?, crop?, format?, deleteSource? }`: `type` é `video` ou `audio`, `trim` é `{ startTime, endTime }`, `crop` é `{ x, y, width, height }`, e `format` é `mp4`, `webm`, `mp3`, `wav`, `m4a` ou `aac`. A rota retorna `{ data: { url, thumbnailUrl, assetId, sizeBytes, mimeType, metadata } }`. `deleteSource: true` exclui a origem depois, quando ela é sua e nada mais a usa.

## Editar áudio
Cada rota retorna um job.

| Método | Caminho | Corpo | O que faz |
| --- | --- | --- | --- |
| `POST` | `/v1/audio-separation` | `{ audioUrl, mode?, quality? }` | Divide uma faixa: `vocal_instrumental` (o padrão) ou `stems` completos, com qualidade `auto`, `fast` ou `best`. Veja [**Separação de áudio** (Audio Separation)](https://nodaro.ai/docs/nodes/audio/audio-separation). |
| `POST` | `/v1/audio-isolation` | `{ audioUrl }` | Mantém a voz principal e remove o fundo. Veja [**Extrator de voz** (Voice Extractor)](https://nodaro.ai/docs/nodes/audio/voice-extractor). |
| `POST` | `/v1/audio-fx` | `{ audioUrl, preset?, mix?, delayMs?, decay?, eqLow?, eqHigh? }` | Um efeito de reverberação, eco, telefone ou megafone. Veja [**Efeitos de áudio** (Audio FX)](https://nodaro.ai/docs/nodes/audio/audio-fx). |
| `POST` | `/v1/mix-audio` | `{ audioUrls, trackVolumes? }` | Sobrepõe de 2 a 20 faixas, cada uma de 0 a 200 por cento. Veja [**Mixar áudio** (Mix Audio)](https://nodaro.ai/docs/nodes/audio/mix-audio). |
| `POST` | `/v1/adjust-volume` | `{ audioUrl or videoUrl, volume?, normalize?, fadeIn?, fadeOut? }` | Muda o nível, normaliza ou aplica fade. Veja [**Ajustar volume** (Adjust Volume)](https://nodaro.ai/docs/nodes/audio/adjust-volume). |
| `POST` | `/v1/combine-audio` | `{ segments: [{ url, startTime?, endTime? }] }` | Junta segmentos um após o outro. Veja [**Combinar áudio** (Combine Audio)](https://nodaro.ai/docs/nodes/audio/combine-audio). |
| `POST` | `/v1/trim-audio` | `{ audioUrl or videoUrl, startTime?, endTime?, audioFormat? }` | Corta áudio, ou extrai o áudio de um vídeo, como `mp3` (o padrão), `wav` ou `aac`. Veja [**Cortar áudio** (Trim Audio)](https://nodaro.ai/docs/nodes/audio/trim-audio). |
| `POST` | `/v1/silence-detect` | `{ audioUrl, thresholdDb?, minSilenceMs?, padMs? }` | Encontra os intervalos de silêncio de uma gravação. 11 créditos. Veja [**Detectar silêncio** (Silence Detect)](https://nodaro.ai/docs/nodes/audio/silence-detect). |
| `POST` | `/v1/audio-sync` | `{ sources: [{ id, url }], reference? }` | Mede as defasagens entre as gravações (de 2 a 6) de uma mesma conversa. Veja [**Sincronizar áudio** (Audio Sync)](https://nodaro.ai/docs/nodes/audio/audio-sync). |

`silence-detect` usa por padrão `thresholdDb: -35`, `minSilenceMs: 700` e `padMs: 120`, e aceita áudio ou vídeo. O `output_data.json` dela é `{ version, ranges: [{ startMs, endMs }], durationMs }`.

`audio-sync` custa 11 créditos por origem depois da primeira: 11 para 2 origens, 33 para 4, 55 para 6. Os IDs precisam ser únicos, e `reference` precisa ser um deles (por padrão, a primeira origem). O `output_data.json` dela é `{ version, reference, offsets: [{ sourceId, offsetMs, confidence, driftMsPerHour }], notes }`, em que um tempo na referência é igual ao tempo na origem mais `offsetMs`. O desvio (drift) é medido e anotado, nunca corrigido.

## Transcrever fala
`POST /v1/transcribe` transforma fala em texto e retorna um job. O corpo é `{ audioUrl, provider?, language?, diarize?, tagAudioEvents?, wordTimestamps? }`, e `audioUrl` também pode ser um vídeo.

| `provider` | Tempo por palavra | Observações |
| --- | --- | --- |
| [`elevenlabs-stt`](https://nodaro.ai/docs/models/audio/elevenlabs-stt) | Sempre | O único mecanismo que respeita `diarize` (quem falou cada palavra) e `tagAudioEvents` (risos, aplausos). |
| [`incredibly-fast-whisper`](https://nodaro.ai/docs/models/audio/incredibly-fast-whisper) | Com `wordTimestamps: true` | Sem a flag, o job é bem-sucedido, é cobrado e retorna só frases. |
| [`whisper`](https://nodaro.ai/docs/models/audio/whisper) | Nunca | Só frases. `wordTimestamps: true` retorna `400 validation_error` antes de qualquer crédito ser gasto. |

Omitir `provider` executa `whisper`, então indique um mecanismo sempre que precisar de marcações de tempo por palavra. O `output_data` do job concluído guarda `text`, `language`, `words` (um `{ text, startMs, endMs, speaker? }` por palavra, em milissegundos), `json` (a transcrição normalizada, também em milissegundos) e, só nos mecanismos mais antigos, `segments` em segundos. `words` entra direto em `captions` no `POST /v1/add-captions`.

**curl**

```bash
curl -X POST https://app.nodaro.ai/v1/transcribe \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "audioUrl": "https://cdn.nodaro.ai/uploads/talk.mp3", "provider": "elevenlabs-stt", "diarize": true }'
```

**TypeScript SDK**

```ts
const { jobId } = await client.audio.transcribe({
audioUrl: 'https://cdn.nodaro.ai/uploads/talk.mp3',
provider: 'elevenlabs-stt',
})
// once completed, burn the words in as kinetic captions
const words = (await client.jobs.get(jobId)).output_data.words
await client.media.addCaptions({
videoUrl: 'https://cdn.nodaro.ai/uploads/talk.mp4',
captions: words,
style: 'word-highlight',
autoTranscribe: false,
})
```

**CLI**

```bash
nodaro audio transcribe --audio https://cdn.nodaro.ai/uploads/talk.mp3 --provider elevenlabs-stt --watch
nodaro jobs get <jobId> --json | jq '.output_data.words' > words.json
nodaro media add-captions https://cdn.nodaro.ai/uploads/talk.mp4 \
  --captions-file words.json --style word-highlight --no-auto-transcribe --watch
```

## Publicar em redes sociais
Os fluxos de conexão abrem janelas pop-up e foram feitos para o app web. A publicação funciona com um token de API pessoal. Os tokens de app OAuth não podem gerenciar conexões.

| Método | Caminho | O que faz |
| --- | --- | --- |
| `GET` | `/v1/social/providers` | As redes aceitas e se cada uma está disponível nesta implantação. |
| `GET` | `/v1/social/connections` | As suas contas conectadas. |
| `DELETE` | `/v1/social/connections/:id` | Desconecta uma conta. |
| `POST` | `/v1/social/telegram/connect` | Conecta o Telegram com um token de bot: `{ botToken }`. |
| `POST` | `/v1/social/connect/custom` | Conecta uma rede que usa campos em vez de login: `{ platform, fields }`. |
| `POST` | `/v1/social/publish` | Publica agora. Retorna um job. 11 créditos. |
| `POST` | `/v1/social/scheduled-posts` | Agenda um post. 11 créditos, cobrados quando ele é publicado. |
| `GET` | `/v1/social/scheduled-posts` | Os seus posts agendados, filtrados por `from`, `to` e `status`. |
| `PATCH` | `/v1/social/scheduled-posts/:id` | Edita um post enquanto ele está `queued` ou `draft`. |
| `DELETE` | `/v1/social/scheduled-posts/:id` | Cancela um post na fila. O histórico dele é mantido. |

`GET /v1/social/providers` lista todas as redes com `{ id, label, connectKind, editor, category, capabilities, available }`. `category` é `social` (um feed em que você posta) ou `publishing` (um site em que você publica artigos). Uma rede que a implantação não configurou aparece com `available: false`, nunca fica oculta. As redes que se conectam com campos (Bluesky, Dev.to, Hashnode, Medium, WordPress e Lemmy) descrevem os campos em `customFields`, e o Nodaro verifica a credencial com a rede antes de salvá-la.

`POST /v1/social/publish` recebe `{ platform, action, connectionId?, caption?, mediaUrl or mediaItems, … }`. Duas falhas exigem tratamentos diferentes:

- **`503 publish_retryable`**: nada foi postado. Enviar a mesma requisição de novo é seguro.
- **`500 publish_failed`**: o resultado é desconhecido. Enviar de novo pode postar duas vezes, então verifique a rede primeiro.

Um post agendado recebe `{ connectionId, action, scheduledAt, caption?, media? }`, em que cada item de mídia é `{ type, r2Key or url }`. A mídia precisa ser de arquivos armazenados nesta implantação, que o Nodaro transforma em links novos quando o post sai; links para outros sites são recusados. Editar um post que já está sendo publicado retorna `409 not_editable`. Veja [**Publicar nas redes** (Publish to Social)](https://nodaro.ai/docs/nodes/publish/publish-to-social) e [Publicar nas redes sociais](https://nodaro.ai/docs/guides/publishing-to-social).

## Equivalentes no SDK e na CLI
| Área | TypeScript SDK | CLI |
| --- | --- | --- |
| Vozes e modificação de voz | `client.voices.list`, `searchLibrary`, `listClones`, `deleteClone`, `change`, `recast`, `analyze`, `exportMix`, `design`, `remix`, `dub` | `nodaro voice list`, `changer`, `recast`, `analyze`, `export`, `design`, `remix`, `dub`, `clones` |
| Mídia | `client.media.downloadVideo`, `downloadVideoProgress`, `videoMetadata`, `trimVideo`, `trimAudio`, `addCaptions`, `stillToVideo`, `slideshow`, `videoOverlay`, `saveToStorage`, `process` | `nodaro media download`, `metadata`, `trim-video`, `trim-audio`, `add-captions`, `still-to-video`, `slideshow`, `video-overlay`, `save` |
| Áudio | `client.audio.separate`, `isolate`, `applyFx`, `mix`, `adjustVolume`, `combine`, `transcribe` | `nodaro audio separate`, `isolate`, `fx`, `mix`, `adjust-volume`, `combine`, `transcribe` |
| Auxiliares de edição | `client.edit.silenceDetect`, `audioSync` | `nodaro edit silence-detect`, `audio-sync` |

Pelo MCP, as mesmas rotas são ferramentas como `voice_changer`, `voice_changer_pro`, `voice_changer_pro_analyze`, `voice_changer_pro_export`, `dubbing`, `transcribe`, `add_captions` e `separate_audio`. Veja a [Referência das ferramentas MCP](https://nodaro.ai/docs/mcp/tools).

## Erros
| Status | Código | Significado |
| --- | --- | --- |
| `400` | `validation_error` | Um campo está ausente ou é inválido, por exemplo `wordTimestamps` no `whisper`, um campo de estilo que exige um estilo de legenda cinético ou uma exportação com todas as faixas sem som. |
| `400` | `provider_not_configured` | A implantação não configurou essa rede social. |
| `401` | `unauthorized` | O token está ausente, é inválido ou foi revogado. |
| `402` | `insufficient_credits` | Só no Nodaro Cloud. A conta não tem créditos para cobrir o job. |
| `404` | `not_found` | Uma rota do Modificador de voz Pro foi chamada em uma implantação que não a oferece. |
| `409` | `not_editable` | O post agendado já está sendo publicado. |
| `410` | `voice_cloning_retired` | A clonagem de voz não é mais oferecida. |
| `413` | — | O trecho dublado tem mais de 30 minutos. |
| `429` | `too_many_downloads` | Já há 4 importações de vídeo em andamento na sua conta. |
| `500` | `publish_failed` | O resultado do post é desconhecido. Verifique a rede antes de enviar de novo. |
| `503` | `publish_retryable` | Nada foi postado. É seguro enviar a mesma requisição de novo. |

## Frequently asked questions

### Como troco as vozes de vários falantes em um vídeo?

Use POST /v1/voice-changer-pro com orderedVoices, uma entrada por falante detectado, na ordem. Uma entrada null mantém a voz original desse falante. Para verificar os falantes antes, execute a etapa analyze, depois faça a troca de voz em stems e exporte a sua própria mixagem.

### Posso clonar uma voz pela API?

Não. A clonagem de voz foi descontinuada, e as rotas de clonagem retornam 410 voice_cloning_retired. Os clones feitos antes continuam funcionando como IDs de voz. Em vez disso, crie uma nova voz a partir de uma descrição com POST /v1/voice-design.

### Qual mecanismo de transcrição retorna marcações de tempo por palavra?

elevenlabs-stt sempre retorna marcações de tempo por palavra, e incredibly-fast-whisper as retorna quando você envia wordTimestamps true. whisper retorna só frases. Omitir provider executa whisper, então indique um mecanismo quando precisar das palavras.

### Qual pode ser a duração de um clipe dublado?

O trecho dublado pode ter até 30 minutos, e o preço é por minuto, com mínimo de 1 minuto. Para uma origem mais longa, duble uma janela com startTime e endTime.

### Quais ferramentas de mídia são gratuitas?

Imagem fixa para vídeo, Apresentação de slides e a rota síncrona de corte e recorte POST /v1/media/process custam 0 créditos. Sobreposição em vídeo custa 22 créditos por execução, e Detectar silêncio, 11 créditos.
