Pausar o anúncio quando o estoque zera
A OTL avisa o seu sistema quando o estoque de um par muda, e ele pausa (ou reativa) o anúncio no marketplace — sem ninguém olhar planilha.
A ideia
Vender um par que acabou é a pior venda: cancelamento, reclamação e nota baixa no marketplace. Este guia liga o estoque da OTL aos seus anúncios:
- Um webhook recebe o aviso
produto.estoque_atualizado. - O seu sistema olha o estoque de cada numeração e pausa ou reativa a variação do anúncio.
- Uma sincronização periódica cobre o que o webhook perder.
Permissão da integração: produtos:ler.
1. Cadastre o webhook
No painel, em Integrações → Webhooks, cadastre o endereço do seu sistema (ou do n8n) e marque:
produto.estoque_atualizado— o estoque de alguma numeração mudou;produto.desativadoeproduto.reativado— o produto saiu ou voltou ao catálogo.
Em De quais produtos, escolha “só o que a minha loja vende” ou informe a lista de SKUs dos seus anúncios. Assim você não recebe aviso de produto que não anuncia.
2. Trate o aviso — nos dois formatos
O mesmo evento chega de duas formas, e o seu sistema precisa tratar as duas:
- avulso, quando um produto muda:
evento: "produto.estoque_atualizado", com o produto emdados.produto; - em lote, quando vários mudam de uma vez (uma carga de estoque):
evento: "produto.lote", com a lista emdados.itens, cada item com o próprioeventoeproduto.
A carga de estoque é o momento em que mais pares zeram. Se o seu sistema só trata o evento avulso, ele funciona no teste e falha no dia em que mais importa.
// Depois de conferir a assinatura (veja "Conferir a assinatura").
function aoReceber(aviso) {
const mudancas =
aviso.evento === 'produto.lote'
? aviso.dados.itens // cada item: { evento, produto, ... }
: [{ evento: aviso.evento, ...aviso.dados }];
for (const mudanca of mudancas) {
if (mudanca.evento === 'produto.desativado') pausarTodasAsVariacoes(mudanca.produto.sku);
else aplicarEstoque(mudanca.produto);
}
}
function aplicarEstoque(produto) {
for (const n of produto.numeracoes) {
// `estoque: null` = a OTL não sabe afirmar. Não é zero: não pause por isso.
if (n.estoque === null) continue;
if (n.estoque === 0) pausarVariacao(produto.sku, n.numeracao);
else reativarVariacao(produto.sku, n.numeracao, n.estoque);
}
}
O que o corpo traz, e por que isso simplifica o seu código:
- O estado completo, não a diferença.
dados.produto.numeracoestem o estoque de todas as numerações, não só da que mudou. Você aplica o que chegou, sem precisar saber o que havia antes. - Responda rápido. Devolva
200assim que conferir a assinatura e guardar o aviso; faça as chamadas ao marketplace depois, numa fila. A OTL espera 10 segundos e, sem resposta, tenta de novo. - O mesmo aviso pode chegar duas vezes. Deduplique pelo cabeçalho
X-OTL-Evento-Id. - A ordem não é garantida. Uma nova tentativa de um aviso antigo pode chegar depois de um mais
novo. Compare
produto.atualizadoEmcom o que você já aplicou, e ignore o mais velho.
3. Quanto anunciar
O estoque da OTL é compartilhado entre todos os parceiros e não é reservado para ninguém. Anunciar a quantidade exata é pedir para vender o mesmo par duas vezes. Duas regras práticas:
- Deixe uma folga. Com 1 ou 2 pares no estoque da OTL, muita gente prefere já pausar a variação.
- Não anuncie mais do que há. Se a OTL tem 4, anunciar 10 é vender sem ter.
const FOLGA = 1;
const quantidadeNoAnuncio = estoque => Math.max(0, estoque - FOLGA);
Mesmo com tudo isso, a conferência que vale é a da criação do pedido — veja Da venda ao pedido.
4. A rede de segurança
Webhook é aviso, não garantia: o seu endereço pode ficar fora do ar, e depois de várias tentativas a entrega é abandonada. Por isso, a cada 15 a 30 minutos, peça o que mudou:
curl "https://api.otlshoes.com.br/v1/produtos?atualizadoDesde=2026-09-29T14%3A00%3A00-03%3A00&limite=100" \
-H "Authorization: Bearer otl_prod_SEU_TOKEN"curl "https://api-sandbox.otlshoes.com.br/v1/produtos?atualizadoDesde=2026-09-29T14%3A00%3A00-03%3A00&limite=100" \
-H "Authorization: Bearer otl_sbx_SEU_TOKEN"const resposta = await fetch('https://api.otlshoes.com.br/v1/produtos?atualizadoDesde=2026-09-29T14%3A00%3A00-03%3A00&limite=100', {
headers: {
Authorization: 'Bearer otl_prod_SEU_TOKEN',
},
});
if (!resposta.ok) {
const { erro } = await resposta.json();
throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/produtos?atualizadoDesde=2026-09-29T14%3A00%3A00-03%3A00&limite=100', {
headers: {
Authorization: 'Bearer otl_sbx_SEU_TOKEN',
},
});
if (!resposta.ok) {
const { erro } = await resposta.json();
throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();<?php
$ch = curl_init('https://api.otlshoes.com.br/v1/produtos?atualizadoDesde=2026-09-29T14%3A00%3A00-03%3A00&limite=100');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer otl_prod_SEU_TOKEN',
]);
$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/produtos?atualizadoDesde=2026-09-29T14%3A00%3A00-03%3A00&limite=100');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer otl_sbx_SEU_TOKEN',
]);
$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}import requests
resposta = requests.get(
"https://api.otlshoes.com.br/v1/produtos",
headers={
"Authorization": "Bearer otl_prod_SEU_TOKEN",
},
params={
"atualizadoDesde": "2026-09-29T14:00:00-03:00",
"limite": 100
},
timeout=30,
)
if not resposta.ok:
erro = resposta.json()["erro"]
raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()import requests
resposta = requests.get(
"https://api-sandbox.otlshoes.com.br/v1/produtos",
headers={
"Authorization": "Bearer otl_sbx_SEU_TOKEN",
},
params={
"atualizadoDesde": "2026-09-29T14:00:00-03:00",
"limite": 100
},
timeout=30,
)
if not resposta.ok:
erro = resposta.json()["erro"]
raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()Testar agora no ambiente de testes
A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.
Aplique o mesmo aplicarEstoque() a cada produto da resposta. Produtos desativados vêm nessa lista
com ativo: false — pause os anúncios deles. O passo a passo está em
Sincronizar catálogo e estoque.
Com o webhook funcionando, essa rotina quase sempre volta vazia. É o que se espera dela.
No n8n
- Webhook (gatilho), método
POST, respondendo “Immediately”. - Code: confira a assinatura — o exemplo pronto está em Conferir a assinatura.
- Code: transforme o aviso numa lista, tratando o lote:
const aviso = $json.body;
const mudancas = aviso.evento === 'produto.lote' ? aviso.dados.itens : [{ evento: aviso.evento, ...aviso.dados }];
return mudancas.flatMap(m =>
m.produto.numeracoes
.filter(n => n.estoque !== null)
.map(n => ({ json: { sku: m.produto.sku, numeracao: n.numeracao, estoque: n.estoque, pausar: n.estoque === 0 } })),
);
- IF
pausar→ o nó do seu marketplace que pausa ou reativa a variação.
Teste no sandbox
- Cadastre o webhook de teste em Integrações → Ambiente de testes → Webhooks.
- Zere uma numeração pelo simulador de estoque.
- Em cerca de 1 minuto o aviso chega. Mude outra numeração do mesmo produto antes disso e veja que chega um aviso, com o estado final.
- O histórico de entregas, no painel, mostra o que o seu endereço respondeu.
Para ver um produto.lote de verdade, basta esperar: o catálogo de teste é atualizado a partir da
produção a cada poucos minutos, e quando vários produtos mudam juntos o aviso vem em lote.
Ver também
- Eventos de webhook — o corpo de cada evento
- Como funcionam os webhooks — tentativas, falhas e o filtro de produtos
- Estoque do produto — a consulta leve, para conferir um par
