Skip to main content
Um banner é o que você diz para alguém dentro do seu produto, onde a pessoa já está: o aviso de manutenção, o trial que acaba em três dias, o prazo da migração, a faixa da promoção. É a coisa mais curta desta API — um título, às vezes um botão — e quase todo o desenho dela é sobre onde aparece e quando para.

Você escolhe o lugar, a gente preenche

Não existe barra flutuante. Nada é injetado no seu layout, e não há opção que faça isso. Você renderiza o componente onde a sua página tem espaço:
Num app Next, importe do @userkit/nextjs — é o mesmo componente, e em modo proxy a leitura e a dispensa são encaminhadas pelos handlers como o resto da superfície do contato, com a sessão num cookie httpOnly da sua própria origem. O banner declara o mesmo slot no painel, e os dois se encontram por string exata. Letras minúsculas, números, hífen e underscore — um nome com maiúscula ou espaço é recusado em vez de normalizado, porque essa string é escrita duas vezes em dois lugares diferentes e maiúscula é como isso dá errado sem ninguém ver. O motivo não é gosto. Um widget que colocasse uma faixa fixa na viewport cobriria o cabeçalho de todo produto que já tem um, em produção, no instante em que alguém salvasse uma linha num painel. Um slot é um lugar que o dono da página declarou ter espaço, e é o único tipo de lugar em que um terceiro pode desenhar. Nada verifica se o seu app ainda renderiza um slot — não vemos o seu código, e um aviso baseado em palpite seria pior que aviso nenhum. O painel oferece os slots que os seus próprios banners já usam.

Um banner por slot

Um slot mostra um banner de cada vez. Quando há mais de um candidato, ganha o de maior priority, e empate vai para o mais novo. Isso é decidido no banco, na mesma instrução que decide todo o resto, então a resposta é idêntica para o componente React, para um fetch de um app mobile e para a prévia do painel. Um cliente que escolhesse o próprio vencedor seria uma segunda opinião que ninguém pediu.

Dois tipos

text é um título, um texto embaixo, um ícone opcional e um botão opcional. O widget desenha com a sua paleta — suas cores, seu raio, sua fonte — para parecer parte do produto e não algo alugado. image é uma arte sua, opcionalmente com link. Não tem título por construção: uma chamada por cima de uma imagem desenhada com a própria chamada são duas chamadas. O arquivo é hospedado por você; a API recebe uma URL http(s) absoluta e não guarda mais nada. Para onde qualquer um dos dois leva é o cta_url — o destino do botão no banner de texto, o link da figura inteira no de imagem. Ele aceita um caminho começando em / (/planos) ou um endereço http(s) completo, as mesmas duas formas que uma notificação no produto aceita — e o caminho é, se tanto, a metade mais natural aqui: um banner é desenhado dentro do seu próprio produto. Um esquema que executa é recusado, e a forma relativa a protocolo //host/caminho também: ela se lê como caminho e nomeia outro host. O image_url é o que continua absoluto — um <img src> relativo resolveria contra qualquer página em que o widget for desenhado. O tipo não muda depois de criado. Os dois preenchem colunas diferentes, e o banco recusa uma linha que carregue as duas — trocar apagaria o que alguém escreveu, sem volta. Crie um banner do outro tipo, o que também mantém a conta de dispensas presa à coisa que as pessoas realmente viram. body é texto puro, não markdown. Um banner que precisa de formatação é um post de changelog com um botão apontando para ele.

Quando aparece

Três coisas decidem, e elas respondem perguntas diferentes: active nasce false: um banner existe enquanto o texto está sendo escrito, e criar um nunca coloca nada na frente de ninguém. É também o botão de desligar, e é por ele que se tira do ar uma faixa da qual você se arrependeu e que já está na tela de alguém — vale na próxima leitura do widget, não numa expiração de cache. ends_at é o que tira a faixa da promoção do ar na segunda sem ninguém lembrar. É o campo mais valioso daqui e o que mais fica vazio. Os dois limites são RFC 3339 com fuso, comparados com o relógio da própria API. Uma data seca seria um instante no fuso que o leitor imaginou, então “a promoção acaba à meia-noite” encerraria na sexta para uns clientes e no sábado para outros.

Quem vê

segment_id é para quem ele é, ou null para todo mundo — o mesmo segmento que as suas feature flags, pesquisas e posts de changelog usam, nunca um filtro próprio. Visitante anônimo não é um caso especial: ele tem um contato como qualquer outro, então vê os banners mirados em todo mundo. Isso é metade do motivo deste módulo existir — o aviso de manutenção na tela de entrada do seu produto. O público nunca viaja até o leitor. “Este banner é mirado num segmento” é a sua própria análise dos seus usuários, e quem está olhando não é para quem essa informação é.

Dispensar

Todos os slots voltam numa chamada só, então uma página com uma faixa no topo e um aviso na cobrança custa uma ida e volta. Ramifique pelo kind, nunca por um campo estar vazio: só os campos do tipo viajam, e é isso que mantém “uma imagem sem descrição” distinguível de “isto não é uma imagem”. A dispensa pertence à pessoa e a acompanha em todos os aparelhos. É idempotente, e dispensar um banner que não existe, que é de outra organização ou que já estava dispensado responde o mesmo 204 — um 404 aqui diria a quem tem a chave publicável se um uuid existe. dismissible: false é para os avisos que são estado do produto e não notícia: “esta conta fica somente leitura até você confirmar o e-mail”. Esconder isso esconderia a explicação do que a pessoa está prestes a encontrar. Dispensar continua sendo registrado e depois ignorado, então nenhum cliente precisa conhecer a regra para evitar um erro — e um banner que você tornar dispensável depois já está fechado para quem tentou. Religar um banner não o mostra de novo para quem já fechou. Dizer a mesma coisa de novo é um banner novo, o que também é o que faz “quantas pessoas fecharam este aqui” ser um número sobre um aviso só.

Alcance

A lista mostra quatro números ao lado de cada banner, dos últimos 30 dias: Um painel que alguém abre onze vezes por dia é onze exibições e um leitor, e o par é justamente o ponto: citar o primeiro como alcance é como um banner parece um sucesso para ninguém. viewers conta todo contato distinto, visitante anônimo incluído, porque o banner é desenhado para ele também — deixar de fora quem ainda não entrou seria deixar de fora exatamente o público a que um aviso costuma se dirigir. Uma exibição é a faixa CRUZAR a dobra, não o componente renderizar. Um banner abaixo da dobra é desenhado a cada carga de página e visto por ninguém, então o <Banner /> reporta a partir de um IntersectionObserver a 50% de visibilidade, uma vez por banner por carga de página — por mais vezes que uma SPA remonte o componente. Os números são null, nunca 0, quando o analytics não pôde ser lido: zero diria que o banner não alcançou ninguém. O engagement.available da resposta diz qual dos dois é. Isso são os seus próprios eventos de analytics: banner.shown e banner.clicked, escritos pelo widget pela mesma porta que os seus track() usam, carregando banner_id, slot e kind — e nenhum texto. Eles aparecem no explorador como qualquer outro evento, que é onde mora o histórico sem limite: os 30 dias do painel são uma janela, e um banner que rodou uma semana em março ficaria com cara de fracasso conforme os zeros de hoje se acumulassem. Não existe tabela de impressões do nosso lado, e isso é deliberado: um contador gravado a cada carga de página pertence ao lugar onde todo contador de carga de página já vai, com o lote e a política de abuso que aquele endpoint já tem. O clique é contado ao lado do seu link e não por um redirecionamento nosso — passar todos os seus botões por esta API faria eles pararem de funcionar quando a gente parasse. A troca é dita e não escondida: um clique que navega antes da fila esvaziar é um clique que ninguém contou, o que é um número um pouco baixo em vez do seu produto quebrado durante um incidente nosso.

O que não existe aqui

E-mail. Um banner é desenhado dentro do seu produto e em nenhum outro lugar. O que se envia é campanha.

Permissões

engagement:manage — owner e admin por padrão, a mesma do onboarding, do changelog e das pesquisas. Leitura incluída: o que você diz para os seus próprios usuários é cópia sua, e nenhuma permissão mais larga mostra isso.