Pular para o conteúdo principal
ℹ️ Conteúdo em atualização

Estamos revisando estes procedimentos. Pessoas técnicas podem encontrar ajustes em códigos de erro ou mapeamentos de payload; valide com os logs e o OpenAPI mais recente. Pessoas não técnicas podem continuar usando o passo a passo e acionar o time de suporte caso algo pareça diferente do descrito.

🎁 Private Label - Promoções

Este guia apresenta as APIs de Promoções do Private Label, que permitem exibir banners promocionais, campanhas de cashback e informações do programa de fidelidade para os clientes.

🎯 Principais Funcionalidades

  • Banners Promocionais: Exibir banners dinâmicos de promoções
  • Campanhas de Cashback: Consultar campanhas ativas com regras e valores
  • Programa de Fidelidade: Acessar dados do programa de pontos/lealdade
  • Gestão de Ofertas: Controlar visualização de ofertas personalizadas
  • Integração com Varejistas: Conectar com sistemas de parceiros

URLs Base:

  • Produção: https://api.credsystem.com.br/private-label-app/api/v1
  • Homologação: https://apihml.credsystem.com.br/private-label-app/api/v1

🔐 Autenticação

Todas as requisições exigem autenticação via Bearer Token (JWT):

Authorization: Bearer <seu_token_jwt>
Atenção

Tokens inválidos ou expirados retornarão erro 401 Unauthorized.


📋 Endpoints Disponíveis

1. Listar Banners Promocionais

Endpoint: GET /banners

Retorna a lista de banners promocionais disponíveis para exibição no aplicativo.

Quando usar

  • Ao carregar a tela principal do app
  • Para exibir carrossel de promoções
  • Ao atualizar ofertas em tempo real

Request

GET /private-label-app/api/v1/promocoes/banners HTTP/1.1
Host: bff-app-privatelabel-hml.credsystem.com.br
Authorization: Bearer <seu_token_jwt>
Accept: application/json

Response - Sucesso (200)

[
{
"url": "https://cdn.exemplo.com/banners/pascoa3.png"
},
{
"url": "https://cdn.exemplo.com/banners/black-friday.png"
}
]

Response - Sem Conteúdo (204)

Quando não há banners disponíveis, a API retorna 204 No Content.

Estrutura da Resposta

CampoTipoDescrição
urlstringURL da imagem do banner

Exemplo cURL

curl -X 'GET' \
'https://bff-app-privatelabel-hml.credsystem.com.br/private-label-app/api/v1/promocoes/banners' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <SEU_TOKEN_JWT>'

2. Consultar Campanhas de Cashback

Endpoint: GET /cashback

Retorna informações detalhadas sobre campanhas de cashback vigentes.

Quando usar

  • Na tela de promoções/ofertas
  • Para exibir regras de cashback
  • Ao verificar elegibilidade do cliente

Request

GET /private-label-app/api/v1/promocoes/cashback HTTP/1.1
Host: bff-app-privatelabel-hml.credsystem.com.br
Authorization: Bearer <seu_token_jwt>
Accept: application/json

Response - Sucesso (200)

{
"nomeLoja": "ZINZANE",
"valorTotalize": "1",
"valorGanha": "10",
"dataInicio": "25/11/2024",
"dataFim": "23/12/2026",
"mensagem": "ou mais",
"teto": "1500",
"tipo": "PORCENTAGEM",
"idCashBackCampanha": "406"
}

Response - Sem Conteúdo (204)

Quando não há campanhas ativas, a API retorna 204 No Content.

Estrutura da Resposta

CampoTipoObrigatórioDescrição
nomeLojastringNome da loja parceira
valorTotalizestringValor necessário de compras para ganhar cashback
valorGanhastringValor do cashback ganho
dataIniciostringData de início da campanha (dd/MM/yyyy)
dataFimstringData final da campanha (dd/MM/yyyy)
mensagemstringTexto auxiliar (ex.: "ou mais")
tetostringValor máximo de cashback acumulável
tipostringTipo da campanha (PORCENTAGEM ou VALOR)
idCashBackCampanhastringIdentificador único da campanha

Exemplo cURL

curl -X 'GET' \
'https://bff-app-privatelabel-hml.credsystem.com.br/private-label-app/api/v1/promocoes/cashback' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <SEU_TOKEN_JWT>'

3. Obter Dados do Programa de Fidelidade

Endpoint: GET /loyalty

Retorna dados do programa de fidelidade/lealdade disponível para o cliente, incluindo informações sobre pontos, módulos e configurações de integração.

Quando usar

  • Ao acessar a seção de fidelidade
  • Para exibir saldo de pontos
  • Ao configurar integrações com parceiros

Request

GET /private-label-app/api/v1/promocoes/loyalty HTTP/1.1
Host: bff-app-privatelabel-hml.credsystem.com.br
Authorization: Bearer <seu_token_jwt>
Accept: application/json

Response - Sucesso (200)

{
"titulo": "Programa de Fidelidade",
"retailerId": "RET001",
"xApiKey": "api-key-12345",
"descricao": "Ganhe pontos em cada compra",
"status": true,
"secretKey": "secret-key-67890",
"statusCode": 200,
"modulos": [
{
"titulo": "Extrato de Pontos",
"icone": "https://cdn.exemplo.com/icons/points.png",
"descricao": "Consulte seu saldo de pontos",
"link": "/pontos/extrato",
"status": true
},
{
"titulo": "Catálogo de Prêmios",
"icone": "https://cdn.exemplo.com/icons/catalog.png",
"descricao": "Troque seus pontos por prêmios",
"link": "/pontos/catalogo",
"status": true
}
]
}

Response - Sem Conteúdo (204)

Quando não há programa de fidelidade configurado, a API retorna 204 No Content.

Estrutura da Resposta

Objeto Principal (Loyalty)

CampoTipoObrigatórioDescrição
titulostringTítulo do programa de fidelidade
retailerIdstringID do varejista no programa
xApiKeystringChave de API para integração
descricaostringDescrição do programa
statusbooleanStatus do programa (true/false)
secretKeystringChave secreta para autenticação
statusCodeintegerCódigo HTTP da resposta
modulosarrayLista de módulos disponíveis (opcional)

Objeto Módulo

CampoTipoObrigatórioDescrição
titulostringTítulo do módulo
iconestringURL do ícone do módulo
descricaostringDescrição do módulo
linkstringLink/rota para acessar o módulo
statusbooleanStatus do módulo (ativo/inativo)

Exemplo cURL

curl -X 'GET' \
'https://bff-app-privatelabel-hml.credsystem.com.br/private-label-app/api/v1/promocoes/loyalty' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <SEU_TOKEN_JWT>'

🎨 Exemplos de Implementação

Exemplo 1: Carrossel de Banners

// Carregar e exibir banners promocionais
async function carregarBanners() {
try {
const response = await fetch(
'https://bff-app-privatelabel-hml.credsystem.com.br/private-label-app/api/v1/promocoes/banners',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Accept': 'application/json'
}
}
);

if (response.status === 204) {
console.log('Nenhum banner disponível no momento');
ocultarCarrossel();
return;
}

if (!response.ok) {
throw new Error(`Erro ao carregar banners: ${response.status}`);
}

const banners = await response.json();
exibirCarrossel(banners);

} catch (error) {
console.error('Erro ao carregar banners:', error);
exibirMensagemErro('Não foi possível carregar as promoções');
}
}

// Exibir carrossel com os banners
function exibirCarrossel(banners) {
const container = document.getElementById('carousel-banners');
container.innerHTML = '';

banners.forEach((banner, index) => {
const slide = document.createElement('div');
slide.className = 'carousel-slide';
slide.innerHTML = `
<img
src="${banner.url}"
alt="Banner promocional ${index + 1}"
loading="lazy"
onerror="this.src='/img/banner-fallback.png'"
/>
`;
container.appendChild(slide);
});

iniciarCarrossel();
}

// Auto-play do carrossel
function iniciarCarrossel() {
let currentSlide = 0;
const slides = document.querySelectorAll('.carousel-slide');

setInterval(() => {
slides[currentSlide].classList.remove('active');
currentSlide = (currentSlide + 1) % slides.length;
slides[currentSlide].classList.add('active');
}, 5000); // Troca a cada 5 segundos
}

// Executar ao carregar a página
document.addEventListener('DOMContentLoaded', carregarBanners);

Exemplo 2: Card de Campanha de Cashback

// Carregar e exibir campanha de cashback
async function carregarCashback() {
try {
const response = await fetch(
'https://bff-app-privatelabel-hml.credsystem.com.br/private-label-app/api/v1/promocoes/cashback',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Accept': 'application/json'
}
}
);

if (response.status === 204) {
console.log('Nenhuma campanha de cashback ativa');
ocultarSecaoCashback();
return;
}

if (!response.ok) {
throw new Error(`Erro ao carregar cashback: ${response.status}`);
}

const campanha = await response.json();
exibirCardCashback(campanha);

} catch (error) {
console.error('Erro ao carregar cashback:', error);
}
}

// Exibir card com informações da campanha
function exibirCardCashback(campanha) {
const container = document.getElementById('cashback-card');

// Calcular dias restantes
const dataFim = parseDate(campanha.dataFim);
const hoje = new Date();
const diasRestantes = Math.ceil((dataFim - hoje) / (1000 * 60 * 60 * 24));

// Formatar valores baseado no tipo
const valorFormatado = campanha.tipo === 'PORCENTAGEM'
? `${campanha.valorGanha}%`
: `R$ ${campanha.valorGanha}`;

container.innerHTML = `
<div class="cashback-card">
<div class="cashback-header">
<h3>💰 Cashback ${campanha.nomeLoja}</h3>
<span class="cashback-badge">${diasRestantes} dias restantes</span>
</div>

<div class="cashback-body">
<div class="cashback-rule">
<p class="rule-label">Compre</p>
<p class="rule-value">R$ ${formatarValor(campanha.valorTotalize)} ${campanha.mensagem || ''}</p>
</div>

<div class="cashback-arrow"></div>

<div class="cashback-reward">
<p class="reward-label">Ganhe</p>
<p class="reward-value">${valorFormatado}</p>
</div>
</div>

<div class="cashback-footer">
<p class="cashback-limit">Limite máximo: R$ ${formatarValor(campanha.teto)}</p>
<p class="cashback-period">
Válido de ${campanha.dataInicio} até ${campanha.dataFim}
</p>
</div>

<button onclick="verDetalhesCashback('${campanha.idCashBackCampanha}')" class="btn-detalhes">
Ver regulamento completo
</button>
</div>
`;
}

// Funções auxiliares
function parseDate(dateString) {
const [dia, mes, ano] = dateString.split('/');
return new Date(ano, mes - 1, dia);
}

function formatarValor(valor) {
return parseFloat(valor).toLocaleString('pt-BR', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}

function verDetalhesCashback(idCampanha) {
// Abrir modal ou navegar para página de detalhes
console.log('Exibir detalhes da campanha:', idCampanha);
window.location.href = `/promocoes/cashback/${idCampanha}`;
}

Exemplo 3: Tela de Programa de Fidelidade

// Carregar programa de fidelidade completo
async function carregarProgramaFidelidade() {
try {
const response = await fetch(
'https://bff-app-privatelabel-hml.credsystem.com.br/private-label-app/api/v1/promocoes/loyalty',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Accept': 'application/json'
}
}
);

if (response.status === 204) {
exibirMensagem('Programa de fidelidade não disponível');
return;
}

if (!response.ok) {
throw new Error(`Erro ao carregar fidelidade: ${response.status}`);
}

const programa = await response.json();

if (!programa.status) {
exibirMensagem('Programa de fidelidade temporariamente indisponível');
return;
}

exibirProgramaFidelidade(programa);
configurarIntegracao(programa);

} catch (error) {
console.error('Erro ao carregar programa de fidelidade:', error);
exibirMensagemErro('Não foi possível carregar o programa de fidelidade');
}
}

// Exibir informações do programa
function exibirProgramaFidelidade(programa) {
const container = document.getElementById('loyalty-container');

container.innerHTML = `
<div class="loyalty-header">
<h2>${programa.titulo}</h2>
<p class="loyalty-description">${programa.descricao}</p>
</div>

<div class="loyalty-modules">
${renderizarModulos(programa.modulos || [])}
</div>

<div class="loyalty-info">
<p class="retailer-id">
<strong>Código do Varejista:</strong> ${programa.retailerId}
</p>
</div>
`;
}

// Renderizar módulos disponíveis
function renderizarModulos(modulos) {
if (!modulos.length) {
return '<p class="no-modules">Nenhum módulo disponível</p>';
}

return modulos
.filter(modulo => modulo.status)
.map(modulo => `
<div class="loyalty-module-card">
${modulo.icone ? `<img src="${modulo.icone}" alt="${modulo.titulo}" class="module-icon" />` : ''}
<h3>${modulo.titulo}</h3>
<p>${modulo.descricao}</p>
<button onclick="acessarModulo('${modulo.link}')" class="btn-module">
Acessar
</button>
</div>
`).join('');
}

// Configurar integração com API do parceiro
function configurarIntegracao(programa) {
// Armazenar credenciais de integração de forma segura
sessionStorage.setItem('loyalty_api_key', programa.xApiKey);
sessionStorage.setItem('loyalty_retailer_id', programa.retailerId);

// NÃO armazenar a secret key no frontend em produção!
// A secret key deve ser usada apenas no backend
console.log('Integração configurada para retailer:', programa.retailerId);
}

// Acessar módulo do programa
function acessarModulo(link) {
// Verificar se é link externo ou interno
if (link.startsWith('http')) {
window.open(link, '_blank');
} else {
window.location.href = link;
}
}

// Fazer requisição para API do parceiro (exemplo)
async function consultarSaldoPontos() {
const apiKey = sessionStorage.getItem('loyalty_api_key');
const retailerId = sessionStorage.getItem('loyalty_retailer_id');

try {
const response = await fetch(`https://api-parceiro.com/pontos/${retailerId}`, {
headers: {
'x-api-key': apiKey,
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});

const dados = await response.json();
exibirSaldoPontos(dados.saldo);

} catch (error) {
console.error('Erro ao consultar saldo de pontos:', error);
}
}

Exemplo 4: Cache Inteligente de Promoções

// Sistema de cache para promoções
class PromocaoCache {
constructor() {
this.CACHE_DURATION = 5 * 60 * 1000; // 5 minutos
this.STORAGE_KEY = 'promocoes_cache';
}

// Salvar no cache
salvar(tipo, dados) {
const cache = this.obterCache();
cache[tipo] = {
dados,
timestamp: Date.now()
};
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(cache));
}

// Buscar do cache
buscar(tipo) {
const cache = this.obterCache();
const item = cache[tipo];

if (!item) return null;

// Verificar se o cache expirou
const idade = Date.now() - item.timestamp;
if (idade > this.CACHE_DURATION) {
delete cache[tipo];
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(cache));
return null;
}

return item.dados;
}

// Obter todo o cache
obterCache() {
try {
return JSON.parse(localStorage.getItem(this.STORAGE_KEY)) || {};
} catch {
return {};
}
}

// Limpar cache específico
limpar(tipo) {
const cache = this.obterCache();
delete cache[tipo];
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(cache));
}

// Limpar todo o cache
limparTudo() {
localStorage.removeItem(this.STORAGE_KEY);
}
}

// Usar cache ao carregar promoções
const cachePromocoes = new PromocaoCache();

async function carregarPromocoesComCache(tipo) {
// Tentar buscar do cache primeiro
const dadosCache = cachePromocoes.buscar(tipo);
if (dadosCache) {
console.log(`${tipo} carregado do cache`);
return dadosCache;
}

// Se não houver cache, buscar da API
const endpoint = obterEndpoint(tipo);

try {
const response = await fetch(endpoint, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Accept': 'application/json'
}
});

if (response.status === 204) {
return null;
}

if (!response.ok) {
throw new Error(`Erro ${response.status}`);
}

const dados = await response.json();

// Salvar no cache
cachePromocoes.salvar(tipo, dados);

return dados;

} catch (error) {
console.error(`Erro ao carregar ${tipo}:`, error);
throw error;
}
}

function obterEndpoint(tipo) {
const baseUrl = 'https://bff-app-privatelabel-hml.credsystem.com.br/private-label-app/api/v1/promocoes';
const endpoints = {
'banners': `${baseUrl}/banners`,
'cashback': `${baseUrl}/cashback`,
'loyalty': `${baseUrl}/loyalty`
};
return endpoints[tipo];
}

// Exemplo de uso
async function inicializarPromocoes() {
try {
const [banners, cashback, loyalty] = await Promise.all([
carregarPromocoesComCache('banners'),
carregarPromocoesComCache('cashback'),
carregarPromocoesComCache('loyalty')
]);

if (banners) exibirCarrossel(banners);
if (cashback) exibirCardCashback(cashback);
if (loyalty) exibirProgramaFidelidade(loyalty);

} catch (error) {
console.error('Erro ao inicializar promoções:', error);
}
}

// Forçar atualização (pull-to-refresh)
function atualizarPromocoes() {
cachePromocoes.limparTudo();
inicializarPromocoes();
}

⚠️ Tratamento de Erros

Erros Comuns

CódigoErroCausaSolução
400Bad RequestAuthorization header ausenteIncluir header Authorization
401UnauthorizedToken inválido ou expiradoRenovar token de autenticação
204No ContentSem promoções/campanhas disponíveisExibir mensagem amigável ao usuário
500Internal Server ErrorErro no servidorTentar novamente mais tarde

Exemplo de Tratamento

async function buscarDadosPromocao(tipo) {
try {
const endpoint = obterEndpoint(tipo);
const response = await fetch(endpoint, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Accept': 'application/json'
}
});

// Sem conteúdo disponível
if (response.status === 204) {
exibirMensagem(`Nenhuma ${tipo} disponível no momento`);
return null;
}

// Token expirado
if (response.status === 401) {
await renovarToken();
return buscarDadosPromocao(tipo); // Tentar novamente
}

// Bad Request
if (response.status === 400) {
const erro = await response.json();
console.error('Erro de requisição:', erro.error.message);
throw new Error(erro.error.message);
}

// Outros erros
if (!response.ok) {
throw new Error(`Erro ${response.status}: ${response.statusText}`);
}

return await response.json();

} catch (error) {
console.error(`Erro ao buscar ${tipo}:`, error);
exibirMensagemErro(`Não foi possível carregar ${tipo}`);
return null;
}
}

🎯 Casos de Uso

1. Tela Inicial com Promoções

Carregar e exibir banners promocionais automaticamente ao abrir o app:

// Inicializar promoções na tela inicial
async function inicializarTelaInicial() {
mostrarLoading();

try {
// Carregar banners em paralelo com outros dados
const [banners, ofertas] = await Promise.all([
carregarPromocoesComCache('banners'),
carregarOutrosConteudos()
]);

if (banners && banners.length > 0) {
exibirCarrossel(banners);
} else {
ocultarSecaoBanners();
}

} catch (error) {
console.error('Erro ao inicializar tela:', error);
} finally {
ocultarLoading();
}
}

2. Seção de Cashback Dinâmica

Exibir campanhas de cashback com contagem regressiva:

// Monitorar e atualizar campanha de cashback
function monitorarCashback() {
carregarCashback();

// Atualizar contagem regressiva a cada minuto
setInterval(() => {
atualizarContagemRegressiva();
}, 60000);

// Recarregar dados a cada hora
setInterval(() => {
cachePromocoes.limpar('cashback');
carregarCashback();
}, 3600000);
}

3. Integração com Programa de Pontos

Conectar com sistema de fidelidade de parceiros:

// Integração completa com programa de fidelidade
async function configurarFidelidade() {
const programa = await carregarPromocoesComCache('loyalty');

if (!programa || !programa.status) {
console.log('Programa de fidelidade indisponível');
return;
}

// Configurar SDK do parceiro
await inicializarSDKParceiro({
apiKey: programa.xApiKey,
retailerId: programa.retailerId
});

// Carregar módulos disponíveis
renderizarModulosFidelidade(programa.modulos);
}

💡 Boas Práticas

1. Cache Eficiente

// Implementar cache com validade
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutos

function verificarCacheValido(timestamp) {
return (Date.now() - timestamp) < CACHE_DURATION;
}

2. Lazy Loading de Imagens

<!-- Carregar imagens sob demanda -->
<img src="${banner.url}" loading="lazy" alt="Banner promocional" />

3. Fallback para Imagens

// Tratamento de erro em imagens
function carregarBannerComFallback(url) {
return `
<img
src="${url}"
onerror="this.src='/img/banner-placeholder.png'"
alt="Banner promocional"
/>
`;
}

4. Pré-carregar Recursos

// Pré-carregar banners ao detectar rede rápida
if (navigator.connection && navigator.connection.effectiveType === '4g') {
preCarregarBanners();
}

5. Analytics de Promoções

// Rastrear visualizações de promoções
function registrarVisualizacaoBanner(bannerUrl) {
analytics.track('banner_visualizado', {
url: bannerUrl,
timestamp: new Date().toISOString()
});
}

6. Segurança de Credenciais

// NUNCA expor secret keys no frontend
// Usar apenas xApiKey para chamadas client-side
// Secret keys devem ficar apenas no backend

// ❌ ERRADO
localStorage.setItem('secret_key', programa.secretKey);

// ✅ CORRETO
// Usar secret key apenas em requisições server-side

📖 Glossário

TermoDefinição
BannerImagem promocional exibida no aplicativo
CashbackDevolução de valor em compras realizadas
CampanhaPromoção com regras e período definidos
LoyaltyPrograma de fidelidade/pontos
Retailer IDIdentificador único do varejista no programa
API KeyChave pública para autenticação em APIs de parceiros
Secret KeyChave secreta para autenticação (uso apenas server-side)
MóduloFuncionalidade específica do programa de fidelidade
TetoValor máximo acumulável em campanha de cashback
Bearer TokenToken JWT usado para autenticação nas requisições

❓ Perguntas Frequentes (FAQ)

1. O que fazer quando não há banners disponíveis?

Quando a API retorna 204 No Content, você deve ocultar a seção de banners ou exibir conteúdo alternativo.

if (response.status === 204) {
ocultarSecaoBanners();
// ou exibir conteúdo padrão
}

2. Como calcular o cashback baseado no tipo?

Verifique o campo tipo:

  • PORCENTAGEM: Multiplique o valor da compra pelo percentual
  • VALOR: Use o valor fixo informado
function calcularCashback(valorCompra, campanha) {
if (campanha.tipo === 'PORCENTAGEM') {
const percentual = parseFloat(campanha.valorGanha);
return (valorCompra * percentual) / 100;
}
return parseFloat(campanha.valorGanha);
}

3. Posso armazenar a Secret Key no localStorage?

NÃO! A secretKey deve ser usada apenas no backend. No frontend, use apenas a xApiKey para requisições permitidas.

4. Com que frequência devo atualizar os banners?

Recomenda-se:

  • Cache de 5-10 minutos para banners
  • Atualização ao retornar para a tela (pull-to-refresh)
  • Recarregar ao detectar mudança de rede

5. Como tratar múltiplas campanhas de cashback?

A API atual retorna um objeto único. Se houver múltiplas campanhas no futuro, implemente:

function exibirMultiplasCampanhas(campanhas) {
return campanhas.map(campanha =>
exibirCardCashback(campanha)
).join('');
}

6. Os módulos do programa de fidelidade são obrigatórios?

Não, o campo modulos é opcional. Sempre verifique sua existência:

const modulos = programa.modulos || [];
if (modulos.length > 0) {
renderizarModulos(modulos);
}

7. Como implementar analytics para promoções?

Rastreie eventos importantes:

// Visualização de banner
analytics.track('banner_viewed', { url: banner.url });

// Clique em campanha de cashback
analytics.track('cashback_clicked', {
campaignId: campanha.idCashBackCampanha
});

// Acesso a módulo de fidelidade
analytics.track('loyalty_module_accessed', {
module: modulo.titulo
});

8. Preciso validar o período da campanha no frontend?

Sim, é uma boa prática exibir status visual:

function verificarCampanhaAtiva(campanha) {
const hoje = new Date();
const inicio = parseDate(campanha.dataInicio);
const fim = parseDate(campanha.dataFim);

return hoje >= inicio && hoje <= fim;
}

📞 Suporte

Para dúvidas ou problemas com a integração, entre em contato com a equipe de suporte técnico.