Crie vídeos pelo n8n, pelo Make ou pelo seu código
A API usa a mesma esteira do app: um tema entra, e saem o MP4 narrado, a legenda, o áudio e o texto de publicação (título, descrição e hashtags), com cada imagem aparecendo na hora em que o narrador fala dela. Você acompanha por consulta ou recebe um webhook quando o vídeo fica pronto.
A API é um produto à parte: não faz parte de nenhum plano e tem saldo próprio, pago por consumo. A Lanterna entrega o arquivo e não publica no YouTube: quem publica é a sua automação, sob a sua responsabilidade.
Comece em 3 passos
- 1. Crie a contaCadastre-se e abra Conta → API. Não precisa de plano.
- 2. Compre créditos e crie uma chaveRecarga a partir de R$ 50. A chave começa com lan_live_ e aparece uma única vez.
- 3. Chame a APIPOST /api/v1/videos com o tema. Receba o vídeo por webhook ou consulte o estado.
Preço e créditos
Você compra créditos pré-pagos e cada vídeo desconta do saldo. A unidade é o token, a mesma do app: 1 token por caractere narrado e 270 por imagem gerada. Texto, montagem e fotos de banco livre não custam. Se uma etapa falhar, os créditos dela voltam sozinhos.
Preço de tabela: R$ 0,50 por 1.000 tokens, com bônus nas recargas maiores:
| Recarga | Tokens | Bônus | Por 1.000 tokens |
|---|---|---|---|
| R$ 50,00 | 100.000 | — | R$ 0,50 |
| R$ 100,00 | 210.000 | +5% | R$ 0,48 |
| R$ 250,00 | 550.000 | +10% | R$ 0,45 |
| R$ 500,00 | 1.150.000 | +15% | R$ 0,43 |
Custo estimado por formato, ao preço de tabela (o gasto real depende do tamanho do roteiro):
format | Duração | Tela | Tokens | Custo |
|---|---|---|---|---|
| shorts | 45 s | 9:16 | 3.618 | R$ 1,81 |
| reels | 30 s | 9:16 | 3.132 | R$ 1,57 |
| youtube_curto | 3 min | 16:9 | 8.802 | R$ 4,40 |
| youtube_medio | 6 min | 16:9 | 14.904 | R$ 7,45 |
| youtube_longo | 12 min | 16:9 | 26.568 | R$ 13,28 |
Autenticação
Envie a chave em todos os pedidos, no cabeçalho Authorization. Guarde-a como uma senha e nunca a coloque em código público. Você pode ter até 10 chaves, revogá-las a qualquer momento e definir um teto diário de gasto por chave.
curl https://SEU-DOMINIO/api/v1/me \ -H "Authorization: Bearer lan_live_..."
{
"account": { "id": "…", "email": "voce@exemplo.com", "name": "Você" },
"key": { "name": "n8n produção", "daily_limit_tokens": null, "used_today_tokens": 0 },
"api_balance_tokens": 210000,
"price_per_1000_tokens_brl": 0.5
}Criar um vídeo
POST /api/v1/videos reserva os créditos, põe o vídeo na fila e responde 202 na hora. A produção leva de alguns minutos (Shorts de 45 s: cerca de 2 minutos; vídeo de 3 minutos: cerca de 10).
curl -X POST https://SEU-DOMINIO/api/v1/videos \
-H "Authorization: Bearer lan_live_..." \
-H "Idempotency-Key: planilha-linha-42" \
-H "Content-Type: application/json" \
-d '{
"title": "O farol que acendeu sozinho",
"theme": "Mistério sobre um farol abandonado na Escócia, tom de documentário.",
"format": "shorts",
"approval": "auto",
"metadata": { "linha": 42 },
"webhook_url": "https://meu-n8n.exemplo.com/webhook/lanterna",
"webhook_events": ["video.completed", "video.failed"]
}'formatescolhe a duração e a proporção (vertical para Shorts e Reels). Em vez dele,flow_idusa um fluxo seu, com as etapas e instruções que você ajustou no app.template_idaplica um template de canal (estilo visual, tom e estrutura) criado em Templates.approval:auto(padrão) roda do tema ao MP4 sem pausas;manualpausa na revisão das cenas e na entrega.metadataé um JSON livre (até 2 KB) devolvido nas consultas e nos webhooks, bom para guardar o id da linha da planilha.Idempotency-Key: repetir a mesma chave devolve o mesmo vídeo (200) em vez de criar outro e cobrar de novo. Use-a sempre: o n8n e o Make reexecutam passos quando algo falha.
{
"id": "6f1c…",
"status": "queued",
"title": "O farol que acendeu sozinho",
"format": "shorts",
"aspect": "9:16",
"approval": "auto",
"stage": "Pesquisa",
"step": 1,
"total_steps": 10,
"tokens_spent": 0,
"estimated_tokens": 3980,
"estimated_cost_brl": 1.99,
"idempotent_replay": false
}Se o saldo disponível (saldo menos o reservado a outros vídeos em andamento) não cobre a estimativa, a API responde 402 insufficient_credits e nada é criado.
Acompanhar e baixar
GET /api/v1/videos/{"{id}"} devolve o estado. O campo status assume:
| queued | Na fila. |
| processing | Em produção. Veja stage, step e total_steps. |
| awaiting_approval | Pausado numa revisão (só no modo manual). |
| completed | Pronto. O campo result traz os arquivos. |
| failed | Falhou. O campo error explica; os créditos das etapas que falharam voltam. Use retry. |
{
"id": "6f1c…", "status": "completed", "stage": "Entrega", "tokens_spent": 3518,
"result": {
"mp4_url": "https://SEU-DOMINIO/api/v1/files/…?exp=…&sig=…",
"srt_url": "…", "audio_url": "…", "thumbnail_url": "…",
"urls_expire_at": "2026-11-07T13:00:00.000Z",
"duration_seconds": 38.1,
"script": "texto narrado…",
"publication_text": "Título, descrição e hashtags…"
}
}Os links de arquivo são assinados e valem por 24 horas: dá para passá-los a outro serviço sem expor a chave. Aceitam Range e ?download=nome.mp4. Baixe e guarde o MP4 no seu armazenamento; consulte de novo o vídeo para obter links novos.
Outras rotas: GET /api/v1/videos (lista, com status, limit e before), GET /api/v1/formats, GET /api/v1/balance e DELETE /api/v1/videos/{"{id}"}.
Modo manual: revisar, devolver, refazer
Com approval: "manual" o vídeo pausa duas vezes (awaiting_approval): depois de planejar as cenas e antes de entregar. Antes de gastar com narração e imagens, veja o plano e o custo do que falta:
curl https://SEU-DOMINIO/api/v1/videos/{id}/scenes -H "Authorization: Bearer lan_live_..."
# { "planned": true, "scenes": [{ "number": 1, "prompt": "…", "excerpt": "trecho da fala…" }, …],
# "estimate": { "narration_tokens": 640, "image_tokens": 2430, "total_tokens": 3070, "cost_brl": 1.54 } }| POST /videos/{id}/approve | Aprova e segue a esteira. |
| POST /videos/{id}/return | Devolve com {"reason": "…", "stage": "Bíblia do projeto"} (stage é opcional). Até 3 devoluções. |
| POST /videos/{id}/scenes/{n}/regenerate | Refaz a imagem da cena n (a partir de 1) por 270 tokens e monta de novo. |
| POST /videos/{id}/retry | Continua um vídeo que falhou, na etapa em que parou. |
| DELETE /videos/{id} | Apaga o vídeo e os arquivos (não vale em produção). |
Webhooks
Em vez de consultar, deixe a Lanterna chamar o seu endereço. Cadastre endereços em Conta → API (ou POST /api/v1/webhooks) e/ou envie webhook_url ao criar um vídeo. Os dois funcionam juntos.
| video.queued | O vídeo entrou na fila. |
| video.progress | Mudou de etapa. |
| video.awaiting_review | Pausou para aprovação (modo manual). |
| video.completed | Pronto. Já traz result com o MP4. |
| video.failed | Falhou. |
{
"id": "e3b0…", // único por evento, igual em reenvios: use para ignorar repetidos
"event": "video.completed",
"created_at": "2026-11-06T13:00:00.000Z",
"data": { …o mesmo JSON de GET /videos/{id}… }
}Confira a assinatura antes de confiar no conteúdo. Todo envio traz Lanterna-Signature: t=<unix>,v1=<hex>, onde v1 é o HMAC-SHA256 de "<t>.<corpo bruto>" com o segredo da conta (whsec_lan_…, em Conta → API, onde também pode ser trocado). Recuse assinaturas com mais de 5 minutos. Também vão Lanterna-Event e Lanterna-Delivery.
const crypto = require('crypto');
function assinaturaValida(segredo, cabecalho, corpoBruto) {
const t = /(?:^|,)t=(\d+)/.exec(cabecalho)?.[1];
const v1 = /(?:^|,)v1=([0-9a-f]+)/.exec(cabecalho)?.[1];
if (!t || !v1) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // recusa assinatura com mais de 5 min
const esperado = crypto.createHmac('sha256', segredo).update(t + '.' + corpoBruto).digest('hex');
return esperado.length === v1.length && crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(v1));
}
// Express: use express.raw({ type: 'application/json' }) para ter o corpo bruto.
app.post('/lanterna', express.raw({ type: 'application/json' }), (req, res) => {
if (!assinaturaValida(process.env.LANTERNA_WEBHOOK_SECRET, req.get('Lanterna-Signature') || '', req.body.toString('utf8'))) {
return res.status(400).send('assinatura inválida');
}
const evento = JSON.parse(req.body.toString('utf8'));
// evento.id é único por evento: guarde para ignorar repetidos
res.sendStatus(200); // responda 2xx em até 10 s
});import hmac, hashlib, time, re
def assinatura_valida(segredo: str, cabecalho: str, corpo_bruto: bytes) -> bool:
t = re.search(r"(?:^|,)t=(\d+)", cabecalho)
v1 = re.search(r"(?:^|,)v1=([0-9a-f]+)", cabecalho)
if not t or not v1 or abs(time.time() - int(t.group(1))) > 300:
return False
esperado = hmac.new(segredo.encode(), t.group(1).encode() + b"." + corpo_bruto, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado, v1.group(1))- Responda 2xx em até 10 segundos. Se falhar, tentamos de novo após 1 min, 5 min, 30 min, 2 h e 12 h; depois disso o envio fica como falho (reenvie por Conta → API ou use
GET /api/v1/webhooks/deliveriespara depurar). Redirecionamentos não são seguidos. - Só https, e o endereço não pode apontar para a rede interna.
- Um vídeo em que se refaz uma cena é concluído de novo: pode chegar mais de um
video.completed. Use oiddo evento para ignorar repetidos. webhook_events(noPOST /videos) filtra o que owebhook_urldo vídeo recebe. No Wait do n8n, use["video.completed", "video.failed"].- Use “Enviar teste” em Conta → API para validar o seu receptor (evento
webhook.test).
Erros e limites
Os erros vêm sempre como { "error": { "code": "…", "message": "…" } }.
| HTTP | code | Quando |
|---|---|---|
| 401 | missing_api_key / invalid_api_key | Sem chave, ou chave inválida ou revogada. |
| 402 | insufficient_credits | Créditos de API insuficientes para o vídeo (ou para refazer/continuar). |
| 404 | not_found | Vídeo, cena ou fluxo inexistente, ou de outra conta. |
| 409 | not_awaiting_approval / not_failed / in_progress / too_many_returns | A ação não vale no estado atual do vídeo. |
| 422 | invalid_request / invalid_webhook_url / invalid_stage | Pedido inválido. |
| 429 | rate_limited / daily_limit_reached | 60 pedidos por minuto por chave (veja Retry-After), ou o teto diário da chave. |
n8n
Não precisa de conector: use o nó HTTP Request. Há dois jeitos, e cada um tem um fluxo pronto para importar (Workflows → Import from file).
A) Criar e esperar no mesmo fluxo (nó Wait)
O HTTP Request cria o vídeo mandando webhook_url: {{ $execution.resumeUrl }} e webhook_events: ["video.completed","video.failed"]; o nó Wait (Resume: On Webhook Call) segura a execução até a Lanterna chamar de volta; um IF separa pronto de falha; o último nó baixa o MP4 pelo link assinado. Use Idempotency-Key: {"{{ $execution.id }}"} para que a reexecução do passo não crie outro vídeo.
Baixar o fluxo “criar vídeo e esperar o MP4”. Depois de importar: crie a credencial Header Auth (nome Authorization, valor Bearer lan_live_…) e troque SEU-DOMINIO pelo endereço.
B) Receber webhooks em outro fluxo (nó Webhook)
Cadastre a URL do nó Webhook em Conta → API. O fluxo de exemplo valida a assinatura (nó Code, com o segredo da conta), separa os eventos e deixa pontos prontos para usar o mp4_url e o publication_text. O nó Code precisa do módulo crypto liberado (NODE_FUNCTION_ALLOW_BUILTIN=crypto).
Baixar o fluxo “receber webhook e validar a assinatura”.
Os dois fluxos foram escritos a partir da documentação do n8n e ainda não foram testados em uma instância do n8n; confira os nós depois de importar.
Make
- Receber o aviso: crie um cenário que começa com Webhooks → Custom webhook, copie a URL gerada e cadastre-a em Conta → API (ou envie como
webhook_url). Rode o cenário uma vez com “Enviar teste” para o Make aprender a estrutura. - Criar o vídeo: em outro cenário, use HTTP → Make a request: URL
https://SEU-DOMINIO/api/v1/videos, método POST, cabeçalhosAuthorization: Bearer lan_live_…,Content-Type: application/jsoneIdempotency-Keycom o id do registro (por exemplo, o id da linha), corpo tipo Raw JSON, e marque Parse response. - Usar o resultado: no cenário do webhook, filtre por
event = video.completede usedata.result.mp4_url(link assinado de 24 h) no próximo módulo, por exemplo HTTP → Get a file para baixar e salvar no Drive. - Segurança: o Make não calcula HMAC sozinho; se quiser validar a assinatura, use um módulo de código ou confie no endereço secreto do webhook (a URL do Custom webhook é imprevisível).
OpenAPI e Postman
A especificação OpenAPI 3.1 é pública em /api/v1/openapi.json. Importe-a no Postman (Import → Link), no Insomnia, ou gere um cliente na linguagem que preferir com um gerador de OpenAPI. Os eventos de webhook estão na seção webhooks da especificação.
Boas práticas
- Sempre envie
Idempotency-Key, com um valor estável por registro (id da linha, id do pedido). - Prefira webhooks ao laço de consulta. Se consultar, espere ao menos 5 segundos entre pedidos.
- Defina um teto diário por chave: se uma automação entrar em laço, o prejuízo fica limitado ao teto e ao saldo comprado.
- Baixe e guarde o MP4 no seu armazenamento: os links duram 24 horas e os vídeos podem ser removidos.
- Revise antes de publicar. A API entrega o arquivo e não publica. O YouTube avalia o canal inteiro e combate conteúdo em massa;
approval: "manual"mantém a revisão humana no fluxo. Leia a política oficial antes de investir. - Proteja a chave e o segredo do webhook como senhas, e troque-os se vazarem.
Dúvidas ou algo que a API ainda não faz? Escreva para o suporte pela sua conta. Voltar ao site