# Trends

> Navegue pelo catálogo de trends do Nodaro, preencha as vagas de uma trend com as suas imagens, faça a cotação de graça e execute via REST, SDK, CLI ou MCP.

Source: https://nodaro.ai/pt-BR/docs/developers/api/trends

A **API de Trends** executa receitas prontas do [Cast Swap](https://nodaro.ai/docs/developers/api/cast-swap). Uma trend é um clipe curto com vagas: as pessoas, os pets, os produtos e o lugar que você pode substituir. Preencha as vagas com as suas próprias imagens e execute a trend para receber o clipe de volta com elas.

As trends fazem parte do Nodaro Cloud. Em uma instalação auto-hospedada, todas as rotas respondem `404`. As rotas recebem um bearer token. Veja [Autenticação](https://nodaro.ai/docs/developers/api/authentication).

Só existem aqui as trends **publicadas**. Um slug de rascunho, não publicado ou desconhecido é um `404` em todas as rotas.

## Endpoints
| Método | Caminho | O que faz |
| --- | --- | --- |
| `GET` | `/v1/trends` | O catálogo, na ordem dele: `{ data: TrendCard[], next }`. Query: `q` (busca), `tag`, `favorites=1` (só as suas), `locale`, `cursor`, `limit` (1 a 60, padrão 30). |
| `GET` | `/v1/trends/:slug` | Uma trend: `{ data: TrendDetail }`, com as vagas, os exemplos, a duração da prévia e as resoluções. Query: `locale`. |
| `POST` | `/v1/trends/:slug/estimate` | Faz a cotação de uma execução. Gratuita. Responde `{ data: { expectedCredits, holdCredits, breakdown } }`. |
| `POST` | `/v1/trends/:slug/runs` | Inicia uma execução. Responde `202 { data: { jobId } }`. Acompanhe a tarefa com `GET /v1/jobs/:id`. |
| `GET` | `/v1/trends/runs` | As suas execuções de trends, das mais recentes para as mais antigas: `{ data: TrendRun[], next }`. Query: `trend` (um slug), `cursor`, `limit` (1 a 60, padrão 30). |
| `PUT` | `/v1/trends/:slug/favorite` | Adiciona a trend aos seus favoritos. Responde `204`. |
| `DELETE` | `/v1/trends/:slug/favorite` | Remove a trend dos seus favoritos. Responde `204`. |

Os nomes, as descrições, os rótulos das vagas e as dicas vêm no idioma de `?locale=` quando ele é um idioma da plataforma. Caso contrário, vêm no idioma do seu perfil e, depois, em inglês.

Uma execução continua legível em `GET /v1/trends/runs` depois que a trend dela passa para uma nova versão ou deixa de ser publicada.

## O que uma trend contém
`GET /v1/trends/:slug` retorna:

- `slots`: a `key`, o `label`, o `kind` (`person`, `pet`, `product` ou `location`), o `required`, uma `hint` opcional e os chips padrão de cada vaga.
- `examples`: o vídeo de resultado de cada exemplo e as imagens que cada vaga usou.
- `previewSec`: `4` quando a trend oferece uma execução de prévia de 4 segundos, senão `null`.
- `resolutions`: as resoluções em que a trend pode ser executada.

Um cartão do catálogo em `GET /v1/trends` traz o slug, o nome, as tags, `needs` (quantas vagas de cada tipo), a duração em segundos e `isFavorite`.

## O corpo da execução
`POST /v1/trends/:slug/estimate` e `POST /v1/trends/:slug/runs` recebem o mesmo corpo: `{ slots, example?, details?, preview?, resolution?, draft?, sessionId?, projectId? }`.

| Campo | O que faz |
| --- | --- |
| `slots` | Obrigatório. Chave da vaga → `{ images?, …chips }`, com 1 a 3 imagens de um só objeto por vaga. Toda vaga obrigatória precisa de imagens. |
| `example` | Opcional. O número de um exemplo, a partir de 1. As imagens dele preenchem as vagas opcionais que você deixa de fora. O padrão é 1. |
| `details` | Opcional. As suas próprias palavras, até 1.000 caracteres. Mencione uma vaga como `{slot:<key>}`. |
| `preview` | Opcional. `true` executa 4 segundos, quando o `previewSec` da trend é 4. |
| `resolution` | Opcional. `"480p"`, o padrão, `"720p"` ou `"1080p"`. |
| `draft` | Opcional. O padrão é `true`. Um rascunho só está disponível em 480p. |
| `sessionId` | Opcional. Aceito e verificado, mas ainda não aplicado: as execuções de trends ainda não são arquivadas em sessões. |
| `projectId` | Opcional. Aceito, mas hoje não é usado pela execução. |

Os chips são os traços próprios do tipo da vaga. Cada chip é `"new"`, feito a partir das suas imagens, ou `"original"`, mantido como no clipe:

| Tipo de vaga | Chips |
| --- | --- |
| `person` | `body`, `outfit`, `hair` |
| `pet` | `body`, `coat` |
| `product` | `shape`, `finish` |
| `location` | Nenhum |

```json
{
"slots": {
"dancer": { "images": ["https://example.com/me.jpg"], "outfit": "original" }
},
"preview": true,
"resolution": "480p"
}
```

## A cotação
A estimativa responde `expectedCredits`, o valor que deve ser cobrado pela execução, e `holdCredits`, o valor reservado quando a execução começa. `breakdown` lista as partes da cotação.

## Repetições
`POST /v1/trends/:slug/runs` aceita um cabeçalho `Idempotency-Key`, de 8 a 200 caracteres. Uma repetição com a mesma chave responde com a tarefa da primeira execução. Nada novo é iniciado nem cobrado.

## Permissões
Um app OAuth precisa de:

- `jobs:read` para ler o catálogo, uma trend e as suas execuções;
- `workflows:execute` para fazer a cotação ou iniciar uma execução, mais `assets:write` para passar um `sessionId`;
- `assets:write` para os favoritos.

## Erros
O corpo de uma recusa é `{ error: { code, message } }`. Os erros de vaga também trazem a chave da vaga como `slot`. As recusas `400` das rotas de cotação e de execução acontecem antes de qualquer cobrança.

| Status | `code` | Quando |
| --- | --- | --- |
| `400` | `validation_error` | O corpo ou uma query não é válido, o `cursor` não foi dado por esta lista, ou o `Idempotency-Key` não tem de 8 a 200 caracteres. |
| `400` | `missing_slot` | Uma vaga obrigatória não tem imagens, ou uma vaga opcional fica vazia e o exemplo escolhido não tem imagens para ela. |
| `400` | `unknown_slot` | `slots` indica uma chave que a trend não tem. |
| `400` | `invalid_choice` | Um chip que o tipo da vaga não aceita, por exemplo um `coat` em uma pessoa ou qualquer chip em um lugar. |
| `400` | `example_out_of_range` | `example` indica um exemplo que a trend não tem, ou uma vaga opcional fica vazia e a trend não tem nenhum exemplo para preenchê-la. |
| `400` | `preview_unavailable` | `preview: true` em uma trend que não oferece prévia. |
| `400` | `invalid_slot_mention` | `details` menciona `{slot:<key>}` com uma chave que a trend não tem. |
| `400` | `prompt_too_long` | `details` é longo demais para esta trend. Encurte o texto. |
| `401` | `unauthorized` | Nenhum usuário autenticado. |
| `404` | `not_found` | A trend não está publicada, ou `sessionId` indica uma sessão que você não pode usar. |
| `502` | `engine_error` | O mecanismo de vídeo respondeu de um jeito inesperado. |
| `503` | `engine_unsupported` | Esta versão da plataforma ainda não consegue executar trends. |
| `503` | `trend_unavailable` | A trend não pode ser executada agora. |
| `503` | `not_available` | Este servidor ainda não tem um catálogo de trends. Todas as rotas, exceto `GET /v1/trends/runs`. |

Quando o mecanismo de vídeo recusa uma cotação ou uma execução, as rotas de trends respondem com os próprios códigos e mensagens fixas:

| Status | `code` | Quando |
| --- | --- | --- |
| `400` | `trend_run_rejected` | O mecanismo recusou estas entradas, por um motivo que não está listado abaixo. |
| `400` | `clip_out_of_range`, `preview_out_of_range`, `video_unreadable` | O próprio clipe da trend não pode ser executado como está. |
| `400` | `unsafe_url` | Uma imagem não é um endereço http ou https público. |
| `400` | `raw_reference_token` | `details` contém `{image:`, `{video:` ou `{audio:`. |
| `402` | `insufficient_credits` | Os seus créditos não cobrem a execução. Em um espaço de trabalho, o limite que a recusou pode responder no lugar: `budget_exceeded`, `member_cap_exceeded` ou `user_allowance_exceeded`. |
| `413` | `storage_limit_exceeded` | O seu armazenamento está cheio. |
| `422` | `job_blocked` | A política de tarefas da implantação bloqueou a execução. |
| `429` | `rate_limited` | Requisições demais. Tente de novo em instantes. |
| `503` | `engine_unsupported`, `estimate_unavailable` | A execução não pode ser executada nem cotada agora. Tente de novo em instantes. |
| `502` | `engine_error` | Qualquer outro caso. |

## Execuções que não terminaram
Uma execução em `GET /v1/trends/runs` que não terminou tem `error: { code, message }`. A mensagem é sempre a mesma frase fixa, então decida pelo código:

| `code` | Significado |
| --- | --- |
| `safety-block`, `policy-block` | A tarefa foi bloqueada. |
| `credits` | A execução ficou sem créditos no meio. Ela foi reembolsada. Faça uma recarga e execute de novo. |
| `cancelled` | A execução foi cancelada. |
| `failed` | Qualquer outra falha. |

Uma execução que não falhou e não foi cancelada tem `error: null`. Uma execução concluída traz `videoUrl`, `posterUrl` e `creditsCharged`. Cada vaga da execução informa se as imagens dela vieram do exemplo (`fromExample`).

## Pelo SDK
`client.trends` encapsula as rotas. Ele também pode ser importado como `@nodaro/sdk/trends`.

```ts
list(params?: ListTrendsParams): Promise<TrendPage<TrendCard>>
get(slug: string, params?: { locale?: string }): Promise<TrendDetail>
estimate(slug: string, request: TrendRunRequest): Promise<TrendEstimate>
run(slug: string, request: TrendRunRequest, options?: { idempotencyKey?: string }): Promise<{ jobId: string }>
runs(params?: ListTrendRunsParams): Promise<TrendPage<TrendRun>>
favorite(slug: string): Promise<void>
unfavorite(slug: string): Promise<void>
```

`list()` e `runs()` resolvem com a página como ela chega, `{ data, next }`. Passe `next` de volta como `cursor`; ele é `null` na última página. `get()`, `estimate()` e `run()` resolvem com o payload, sem o envelope `{ data }`. `"runs"` nunca é um slug: os métodos que recebem um slug lançam um `TypeError` para ele antes de qualquer requisição.

```ts

const { data: cards } = await client.trends.list({ q: "dance" })
const trend = await client.trends.get(cards[0].slug)

const request: TrendRunRequest = {
// Every required slot needs 1–3 pictures; optional slots left out take the example's.
slots: Object.fromEntries(
trend.slots.filter((s) => s.required).map((s) => [s.key, { images: [myPhotoUrl] }]),
),
}
const quote = await client.trends.estimate(trend.slug, request)
console.log(`about ${quote.expectedCredits} credits, ${quote.holdCredits} held while it runs`)

const { jobId } = await client.trends.run(trend.slug, request, { idempotencyKey: crypto.randomUUID() })
const job = await client.jobs.get(jobId)
```

## Pela CLI
```bash
nodaro trends list --search dance
nodaro trends get <slug>
nodaro trends estimate <slug> --slot dancer=https://example.com/me.jpg --choice dancer.outfit=original
nodaro trends run <slug> --slot dancer=https://example.com/me.jpg --idempotency-key <key>
nodaro trends runs --trend <slug>
nodaro trends favorite <slug>
```

`--slot` se repete, com 1 a 3 imagens por vaga. `trends run` gasta créditos, então execute `trends estimate` antes. Veja [Comandos](https://nodaro.ai/docs/developers/cli/commands).

## Pelo MCP
Quatro ferramentas MCP cobrem as trends: `list_trends`, `get_trend`, `estimate_trend` e `run_trend`. Veja [MCP › Vídeo](https://nodaro.ai/docs/mcp/tools/video#trends). `run_trend` sem `confirm` retorna a cotação e não cobra nada. Com `confirm: true`, depois que o usuário aceita o preço, ela inicia a execução e retorna um ID de tarefa.

## Perguntas frequentes

### O que é uma trend no Nodaro?

Uma trend é uma receita pronta do Cast Swap. É um clipe curto com vagas para as pessoas, os pets, os produtos e o lugar que você pode substituir. Você preenche as vagas com as suas próprias imagens e recebe o clipe de volta com elas.

### Como sei quanto custa uma execução de uma trend?

Chame POST /v1/trends/:slug/estimate com o mesmo corpo que você vai executar. A chamada é gratuita. Ela retorna expectedCredits, o valor que deve ser cobrado pela execução, e holdCredits, o valor reservado enquanto ela roda.

### Preciso preencher todas as vagas?

Não. Toda vaga obrigatória precisa de 1 a 3 imagens. Uma vaga opcional que você deixa de fora recebe as imagens do exemplo da trend, o primeiro por padrão.

### Posso tentar de novo uma execução de trend com segurança?

Sim. Envie o mesmo cabeçalho Idempotency-Key, de 8 a 200 caracteres. Uma repetição com a mesma chave responde com a tarefa da primeira execução, e nada novo é iniciado nem cobrado.

### As trends funcionam em uma instalação auto-hospedada?

Não. As trends fazem parte do Nodaro Cloud. Em uma instalação auto-hospedada, todas as rotas de trends respondem 404.
