Skip to content

Embeds e white-label ​

Qualquer jornal, ONG ou microsite governamental pode exibir um dado real e citado do Futuros, o valor mais um chip de fonte com deep link para a fonte primária, com um único <iframe>. O contrato de honestidade viaja com o embed: um embed nunca mostra um número sem sua procedência.

Três rotas de widget mais um builder:

RotaRenderiza
/embed/statUma estatística citada — valor grande + unidade, chip de delta com sinal, chip de fonte, marca
/embed/chartO mesmo indicador como um pequeno gráfico de linha (último ponto rotulado)
/embed/boardUma grade responsiva com vários quadros stat/chart — um dashboard inteiro em um iframe
/incrustarA UI do builder em ES — escolha as opções, veja a prévia ao vivo, copie o snippet

Todas as rotas de embed são embutíveis cross-origin: o vercel.json serve /embed/(.*) com a mesma Content-Security-Policy aplicada ao resto do app (default-src 'self', script-src e connect-src restritos, object-src 'none', base-uri 'self', form-action 'self'), relaxando apenas frame-ancestors para *. O resto do site mantém frame-ancestors 'self' mais X-Frame-Options: SAMEORIGIN. O chrome do app (navegação, onboarding) é desativado sob /embed, então o widget pinta apenas a si mesmo.

/embed/stat e /embed/chart ​

Parâmetros de query compartilhados:

ParâmetroValoresPadrãoNotas
paramslug do pilar (salud, educacion, …)saludqual pilar
isoISO3 (MEX, BRA, …) ou LATAMMEXconvertido para maiúsculas automaticamente; LATAM renderiza o agregado regional ("América Latina y el Caribe")
indicatorid do indicadorprimeiro indicador da célulaescolha uma métrica específica
themelight | darkdark
langes | en | ptesstrings trilíngues completas + rótulos/unidades de métricas localizados
accentcor hex #RGB–#RRGGBBAAazul Oxford (#11457e claro / #6496d5 escuro)acento white-label (deve casar com /^#[0-9a-fA-F]{3,8}$/); sobrescreve tanto o acento quanto a cor de delta positivo

O valor, o delta (vs. a média de 3 anos), a unidade e o chip de fonte vêm do mesmo payload estático de célula que o app usa; o chip de fonte aponta para a fonte primária do indicador e a marca aponta de volta para a célula viva do atlas.

Quatro mecânicas por trás desse parágrafo (todas em src/routes/_embed-shared.tsx):

  • Gate somente-estático. fetchParameter() carrega a célula gerada no build /data/parameter-cache/<param>__<ISO>.json e reporta source: "static" | "mock" mais status: ready | not_generated | quarantined. Em um cache miss, o app principal pinta um mock determinístico para manter o layout vivo; o embed trata qualquer coisa que não seja source:"static": ou que seja not_generated: como sem dados, porque um valor mock dentro de um iframe de terceiro seria um número fabricado vestindo um chip de fonte real.
  • Ordem de resolução do chip de fonte. Três passos, o primeiro acerto vence: campos inline no indicador (source, source_url, vintage_year) → a linha citations[] do próprio payload casada por citation_id → o registro de fontes (inferSourceKey(citation_id) → metadados da instituição → template de URL deepLinkFor()). O passo do registro é a mesma maquinaria de syntheticCitation(): o chip pode ser reconstruído apenas a partir do id, e somente um id que não mapeia para nenhuma instituição conhecida não produz nada.
  • Rótulos. Os payloads gerados no build carregam apenas rótulos/unidades canônicos em ES; ?lang=en|pt os sobrepõe a partir do registro de métricas gerado no build, carregado de forma assíncrona e cacheado em nível de módulo. O texto ES pinta imediatamente e permanece se esse fetch nunca resolver, e o embed honra ?lang= exatamente, ao contrário do app principal, nunca recai na preferência de idioma armazenada do visitante.
  • A cor do delta é sensível à direção. O chip colore pelo good_direction do indicador (up / down / neutral): um valor em queda de uma métrica onde cair é bom renderiza como bom; neutral renderiza cinza.

/embed/chart exige adicionalmente uma series não vazia no indicador: um valor citado sem histórico renderiza o estado sem-dados, nunca um gráfico vazio. O último ponto é marcado com um ponto de referência e repetido como valor de destaque.

Embed mínimo de estatística:

html
<iframe
  src="https://futuros.xyz/embed/stat?param=salud&iso=MEX&theme=light&lang=pt&et=ft_embed_SEU_TOKEN"
  width="320" height="180" style="border:0" loading="lazy"
  title="Futuros — Saúde, México"></iframe>

Embed de gráfico white-label (acento customizado, português, indicador específico):

html
<iframe
  src="https://futuros.xyz/embed/chart?param=educacion&iso=BRA&indicator=se_xpd_totl_gd_zs&theme=dark&lang=pt&accent=%23004b87&et=ft_embed_SEU_TOKEN"
  width="360" height="240" style="border:0" loading="lazy"
  title="Futuros — Gasto em educação, Brasil"></iframe>

Note o # codificado na URL (%23). Os tamanhos sugeridos seguem o builder /incrustar: stat 320×180, chart 360×240.

/embed/board — dashboard multi-quadro ​

Um único iframe renderiza uma grade responsiva e tematizada de quadros stat/chart. Um parceiro (BID / CAF / CELAC) insere um único iframe e recebe um dashboard inteiro, white-label, sem mudança de código.

Parâmetros:

ParâmetroValoresPadrãoNotas
panelskind:param:iso[:indicator] separados por vírgula—kind = stat | chart; até 12 quadros
themelight | darkdarktematiza o shell e cada quadro
accentcor hex—repassado a cada quadro
brandtexto livreFuturossubstitui a marca; com um brand customizado a atribuição no rodapé lê "powered by Futuros ↗", caso contrário lê "futuros.xyz ↗"
langes | en | ptesencaminhado a cada quadro

Cada spec de quadro é kind:param:iso[:indicator]: p. ex. stat:salud:MEX ou chart:educacion:BRA:se_xpd_totl_gd_zs. O board monta cada quadro como um iframe same-origin de /embed/stat ou /embed/chart, encaminhando theme, lang e accent.

O parsing degrada campo a campo, nunca o board inteiro: um kind desconhecido vira stat, um param vazio vira salud, um iso vazio vira MEX (sempre em maiúsculas), e qualquer coisa além de 12 quadros é truncada silenciosamente. Os quadros renderizam como iframes de altura fixa (stat 160 px, chart 220 px) em uma grade auto-fill com coluna mínima de 260 px, então o board reflui com a largura da página hospedeira.

Um dashboard de país white-label:

html
<iframe
  src="https://futuros.xyz/embed/board?panels=stat:salud:MEX,chart:educacion:MEX,stat:seguridad:MEX,chart:trabajo-economia:MEX&theme=light&accent=%23004b87&brand=IDB&lang=pt&et=ft_embed_SEU_TOKEN"
  width="100%" height="640" style="border:0" loading="lazy"
  title="Dashboard Futuros — México"></iframe>

Se panels estiver vazio, o board mostra uma dica (?panels=stat:salud:MEX,chart:educacion:BRA) em vez de um frame em branco.

Tokens de embed ​

Desde a release de contas, o futuros.xyz exige sessão em toda rota do app, e um iframe em um site de terceiros não pode carregar esse cookie. Um token de embed o substitui:

  1. Crie o token em /cuenta/embeds: um rótulo, as rotas permitidas (/embed/stat, /embed/chart, /embed/board; o board inclui stat e chart porque os monta como filhos) e a validade (padrão 365 dias, máximo 730). Mostrado uma única vez; formato ft_embed_<43 caracteres>.
  2. Acrescente &et=<token> à URL do iframe (o builder /incrustar faz isso por você se colar o token). O widget reenvia o mesmo token como cabeçalho X-Embed-Token nas leituras de dados, e o board o propaga a cada painel.
  3. Revogue na mesma página. O edge guarda o veredito positivo por 60 s, então um iframe revogado deixa de carregar em menos de um minuto. Um token expirado, revogado, desconhecido ou de uma rota fora do seu escopo responde 401 com uma linha de texto que aponta para /cuenta/embeds.

O token abre apenas as rotas /embed/* do seu escopo e os arquivos de dados que esses widgets leem (/data/parameter-cache/<parâmetro>__<país>.json e /data/metrics/registry.json), não o resto do corpus.

SituaçãoResposta do edge
et válido em uma rota dentro do escopoo widget, como antes
Sem et401 (texto)
et de outra rota (p. ex. um token só-stat em /embed/board)401
Arquivo de dados fora dos dois acima401
Verificador indisponível503 + Retry-After: 30

O token identifica a conta que o criou; cada criação e revogação fica no registro de auditoria, e os tokens de uma conta excluída deixam de valer. Só o hash é armazenado (SHA-256 com pimenta): perdê-lo obriga a criar outro. A CSP de /embed/* não muda: continua com frame-ancestors *.

/incrustar — o builder ​

https://futuros.xyz/incrustar?lang=pt é o builder sem código para os embeds de widget único. Escolha pilar, país, indicador, tipo (stat/chart) e tema; ele mostra uma prévia ao vivo em iframe do valor citado real e entrega um snippet de uma linha para copiar e colar. O dropdown de indicadores é populado com os indicadores reais da célula selecionada, então você só embute uma métrica que existe.

Tematização — semântica de override de themeTokens ​

Cada widget aplica inline um conjunto completo de variáveis CSS na própria raiz (themeTokens() em _embed-shared.tsx), não há classe de tema global, então os estilos da página hospedeira não vazam para dentro e o widget não vaza para fora:

TokenPapel?accent= sobrescreve?
--e-bg / --e-panel / --e-lineFundo, cartão, bordaNão
--e-ink / --e-ink-2 / --e-ink-3Hierarquia de textoNão
--e-accentMarca, traço da linha do gráficoSim
--e-goodDelta na direção boaSim — o mesmo hex do acento
--e-badDelta na direção ruim, ressalva de quarentenaNão — permanece vermelho
--e-citeChip de citaçãoNão

O override é deliberadamente parcial: accent substitui exatamente --e-accent e --e-good, nunca --e-bad ou --e-cite. Um parceiro pode aplicar sua marca ao widget e aos deltas positivos; a cor de aviso e o chip de citação não são personalizáveis, você pode fazer white-label do número, não da ressalva.

Garantias de honestidade ​

O contrato de procedência tem dentes dentro do próprio widget:

  • Nenhum número fabricado. Um embed se recusa a renderizar payloads mock/de fallback, se a fonte de dados da célula não for o corpus estático, ele mostra "Dados não disponíveis" em vez de um valor inventado.
  • A quarentena é visível. Uma célula sinalizada pelos validadores renderiza com uma ressalva explícita de "em revisão" em vez de apresentar silenciosamente um número posto em dúvida.
  • A citação viaja junto. O chip de fonte é reconstruído apenas a partir do citation_id (veja Procedência), então o deep link funciona mesmo que o embed nunca carregue o registro completo de citações.

Framing e segurança ​

  • As rotas de embed enviam a mesma CSP completa do app, com frame-ancestors * como única diferença: default-src 'self', script-src e connect-src limitados a self mais as origens de PostHog, jsDelivr e Google Fonts, object-src 'none', base-uri 'self', form-action 'self'. Nada muda para quem embute (o iframe continua funcionando em qualquer página hospedeira), mas a superfície de embed não roda mais sem restrições de conteúdo.
  • Todas as outras rotas enviam frame-ancestors 'self' e X-Frame-Options: SAMEORIGIN, então o app principal não é embutível fora da origem.
  • Embeds exigem um token de embed (?et= ou o cabeçalho X-Embed-Token); ver Tokens de embed. Continuam sendo GETs de dados estáticos: não há nada a submeter.
  • Os widgets são autocontidos: o tema é aplicado via overrides inline de variáveis CSS na raiz do embed (não uma classe global), então o mesmo número lê corretamente dentro de qualquer página hospedeira.

Os valores por trás de cada embed são as mesmas observações citadas expostas pela API Pública v1: o embed é apenas uma vista renderizada e linkável de uma célula.

Cada número com sua fonte — a rastreabilidade é o contrato.