Esta receita cobre o caso real: você tem 100.000 produtos com 300.000 imagens
no seu sistema e precisa carregar tudo na Voop pela primeira vez.
Estimativas
- Metadados: ~5-10 minutos para 100k items (depende de DB load)
- Imagens: ~30-60 minutos para 300k imagens (limitado por per-domain semaphore)
- Total: ~1 hora; produtos ficam consultáveis em ~10min, imagens chegam após
Pré-requisitos
- API key com escopos
catalog:write, bulk_import:manage
- Suas imagens hospedadas em URL pública (CDN, S3 público, etc.)
- Espaço em disco para gerar o JSONL
Passo 1: gere o JSONL
Idealmente, declare suas categorias e brands antecipadamente no dashboard. Caso
contrário, a Voop cria automaticamente conforme aparecem.
import { createWriteStream } from 'fs';
import { db } from './seu-db.js';
const stream = createWriteStream('voop-import.jsonl');
for await (const p of db.iterateProducts()) {
const line = {
externalSystem: 'meu-erp',
externalId: p.id,
sku: p.sku,
name: p.name,
description: p.descriptionHtml,
price: p.price,
cost: p.cost,
weight: p.weightKg,
gtin13: p.ean,
ncm: p.ncm,
category: { name: p.categoryName },
brand: p.brandName ? { name: p.brandName } : undefined,
tags: p.tags,
metadata: { supplierId: p.supplierId },
media: p.images.map((img, i) => ({
url: img.publicUrl,
role: i === 0 ? 'primary' : 'gallery',
sortOrder: i,
})),
};
stream.write(JSON.stringify(line) + '\n');
}
stream.end();
console.log('Done — voop-import.jsonl');
Para 100k items, o arquivo gera em ~30s e fica entre 30-80 MB (bem abaixo do
limite de 100 MB). Se passar, divida em 2-3 jobs.
Passo 2: inicie o job
JOB=$(curl -sX POST https://api.voop.work/api/v1/catalog/imports \
-H "Authorization: Bearer $VOOP_KEY" \
-H "Content-Type: application/json" \
-d "{ \"fileSizeBytes\": $(stat -c%s voop-import.jsonl) }")
JOB_ID=$(echo $JOB | jq -r .data.jobId)
UPLOAD_URL=$(echo $JOB | jq -r .data.uploadUrl)
echo "Job: $JOB_ID"
Passo 3: faça upload do JSONL
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/x-ndjson" \
--data-binary @voop-import.jsonl
Passo 4: dispare o processamento
curl -X POST https://api.voop.work/api/v1/catalog/imports/$JOB_ID/start \
-H "Authorization: Bearer $VOOP_KEY"
Passo 5: poll de status
while true; do
STATUS=$(curl -s https://api.voop.work/api/v1/catalog/imports/$JOB_ID \
-H "Authorization: Bearer $VOOP_KEY" | jq -r .data.status)
PROGRESS=$(curl -s https://api.voop.work/api/v1/catalog/imports/$JOB_ID \
-H "Authorization: Bearer $VOOP_KEY" | jq -r .data.progress)
echo "[$STATUS] $PROGRESS%"
case $STATUS in
completed|completed_with_errors|failed|cancelled) break ;;
esac
sleep 10
done
Ou configure um webhook import.completed antes de chamar start.
Passo 6: revise os erros
Se status === "completed_with_errors":
curl https://api.voop.work/api/v1/catalog/imports/$JOB_ID/errors \
-H "Authorization: Bearer $VOOP_KEY" | jq
Para cada erro, você verá:
lineNumber — qual linha do seu JSONL
externalId — qual produto (se extraído antes do erro)
errorType — validation (campo inválido), conflict (SKU duplicado), database
errorMessage — descrição
rawLine — primeiras 4kb do JSON original (para debug)
Corrija seu gerador, gere um JSONL só com os items que falharam, e re-importe —
itens que já entraram retornarão unchanged (zero impacto).
Passo 7: monitore a ingest de imagens
Os items ficam consultáveis na API e na IA imediatamente após o import dos
metadados — antes mesmo das imagens chegarem.
Para monitorar imagens, configure um webhook subscrito a media.ingested e
media.ingestion_failed. Em ~30-60 minutos a fila normalmente esgota.
Se uma imagem falha (CDN cliente offline, URL quebrada), o evento
media.ingestion_failed te avisa com o motivo.
Passo 8 (opcional): re-sync periódico
Configure um cron diário para re-importar o catálogo inteiro. Graças ao
contentHash, apenas itens que mudaram serão tocados — o resto retorna
unchanged em milissegundos.
# crontab
0 3 * * * /opt/voop-sync/run-import.sh
Não precisa fazer diff manual — o backend faz por você.