API de automação

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. 1. Crie a contaCadastre-se e abra Conta → API. Não precisa de plano.
  2. 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. 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:

RecargaTokensBônusPor 1.000 tokens
R$ 50,00100.000—R$ 0,50
R$ 100,00210.000+5%R$ 0,48
R$ 250,00550.000+10%R$ 0,45
R$ 500,001.150.000+15%R$ 0,43

Custo estimado por formato, ao preço de tabela (o gasto real depende do tamanho do roteiro):

formatDuraçãoTelaTokensCusto
shorts45 s9:163.618R$ 1,81
reels30 s9:163.132R$ 1,57
youtube_curto3 min16:98.802R$ 4,40
youtube_medio6 min16:914.904R$ 7,45
youtube_longo12 min16:926.568R$ 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.

Teste a conexão
curl https://SEU-DOMINIO/api/v1/me \
  -H "Authorization: Bearer lan_live_..."
Resposta
{
  "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).

Pedido
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"]
  }'
  • format escolhe a duração e a proporção (vertical para Shorts e Reels). Em vez dele, flow_id usa um fluxo seu, com as etapas e instruções que você ajustou no app.
  • template_id aplica um template de canal (estilo visual, tom e estrutura) criado em Templates.
  • approval: auto (padrão) roda do tema ao MP4 sem pausas; manual pausa 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.
Resposta (202)
{
  "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:

queuedNa fila.
processingEm produção. Veja stage, step e total_steps.
awaiting_approvalPausado numa revisão (só no modo manual).
completedPronto. O campo result traz os arquivos.
failedFalhou. O campo error explica; os créditos das etapas que falharam voltam. Use retry.
Vídeo pronto (resumo)
{
  "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}/approveAprova e segue a esteira.
POST /videos/{id}/returnDevolve com {"reason": "…", "stage": "Bíblia do projeto"} (stage é opcional). Até 3 devoluções.
POST /videos/{id}/scenes/{n}/regenerateRefaz a imagem da cena n (a partir de 1) por 270 tokens e monta de novo.
POST /videos/{id}/retryContinua 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.queuedO vídeo entrou na fila.
video.progressMudou de etapa.
video.awaiting_reviewPausou para aprovação (modo manual).
video.completedPronto. Já traz result com o MP4.
video.failedFalhou.
Corpo de um evento
{
  "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 "&lt;t&gt;.&lt;corpo bruto&gt;" 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.

Node.js
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
});
Python
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/deliveries para 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 o id do evento para ignorar repetidos.
  • webhook_events (no POST /videos) filtra o que o webhook_url do 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": "…" } }.

HTTPcodeQuando
401missing_api_key / invalid_api_keySem chave, ou chave inválida ou revogada.
402insufficient_creditsCréditos de API insuficientes para o vídeo (ou para refazer/continuar).
404not_foundVídeo, cena ou fluxo inexistente, ou de outra conta.
409not_awaiting_approval / not_failed / in_progress / too_many_returnsA ação não vale no estado atual do vídeo.
422invalid_request / invalid_webhook_url / invalid_stagePedido inválido.
429rate_limited / daily_limit_reached60 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

  1. 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.
  2. Criar o vídeo: em outro cenário, use HTTP → Make a request: URL https://SEU-DOMINIO/api/v1/videos, método POST, cabeçalhos Authorization: Bearer lan_live_…, Content-Type: application/json e Idempotency-Key com o id do registro (por exemplo, o id da linha), corpo tipo Raw JSON, e marque Parse response.
  3. Usar o resultado: no cenário do webhook, filtre por event = video.completed e use data.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.
  4. 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