Skip to content
Vai integrar o Smartflow do SDK web com ajuda de IA? Copie a instrução que aponta o modelo para esta documentação.
Ver a instrução
Você vai integrar o Smartflow do SDK web. Use a documentação oficial como fonte da verdade.

- Página desta integração: https://developers.zarv.com/sdk/smartflow
- Contrato do SDK e instalação do loader: https://developers.zarv.com/sdk/
- Índice da documentação em markdown: https://developers.zarv.com/llms.txt
- Documentação completa em um arquivo: https://developers.zarv.com/llms-full.txt

O SDK é imperativo: carregue https://js.zarv.com/sdk.js e chame Zarv('smartflow', config) — um verbo e um objeto de config. Não crie um global novo, outra tag de script, nem varredura declarativa de atributos. Chamadas feitas antes do bundle carregar são enfileiradas: não espere nenhum evento de 'ready'. O SDK não tem ambiente de desenvolvimento: use https://js.zarv.com/sdk.js também em localhost, e separe testes por chave pública.

Antes de escrever qualquer código, leia a página desta integração. Siga exatamente os nomes de campos, endpoints, callbacks e formatos que estiverem documentados — não invente parâmetros nem endpoints, e não deduza comportamento a partir de outras APIs que você conhece. Se algo de que você precisa não estiver na documentação, diga que não está em vez de supor.

Minha tarefa: [ex.: validar telefone, e-mail, facial e CNH dentro do meu formulário de cadastro]

Smartflow

O Smartflow é o funnel de cadastro/KYC embutido do SDK Zarv. Sua configuração é imperativa: você chama Zarv('smartflow', config), que monta o funnel como um iframe na sua página.

🎮 Abrir o playground → — um embed real, com a sua chave de campanha: teste tela isolada, prefill, modal, tema, identify e complete, e veja cada callback disparar.

Configuração básica

html
<div id="zarv-smartflow"></div>
<script async src="https://js.zarv.com/sdk.js"></script>
<script>
  Zarv("smartflow", {
    publicKey: "pk-XXXXXXXX", // a chave pública do flow (admin Zarv)
    mount: "#zarv-smartflow", // embed inline; ou mode: 'modal'
    onComplete: ({ leadId }) => console.log("finalizou", leadId),
    onDecision: ({ leadId, status }) => console.log("decisão", status), // approved | rejected | manual
  });
</script>

O campo publicKey é obrigatório: é a chave pública do flow, no formato pk-<id>, obtida no admin Zarv (aba Avançado da campanha). Ela pode ser regenerada no admin — ao regenerar, a chave antiga para de funcionar imediatamente e qualquer embed que ainda a use deixa de carregar até você atualizar o snippet.

Opções de configuração

CampoTipoDescrição
publicKeystringObrigatório. Chave pública do flow (pk-<id>) do admin.
mountstringSeletor CSS do elemento onde o funnel é embutido inline.
mode'inline' | 'modal'Forma de exibição: embutido na página ou em modal centralizada.
heightnumberAltura, em px, do embed inline. Por padrão há auto-resize ao conteúdo.
modal{ width?, height?, margin? }Ajustes da modal. A modal é sempre centralizada; por padrão ajusta à altura do conteúdo (auto-resize). height fixa a altura.
screen'face' | 'cnh' | 'address' | 'email' | 'phone'Embute UMA tela isolada em vez do fluxo inteiro. Omita para o fluxo completo.
leadIdstringUsa um lead existente. Omita para iniciar uma auto-sessão (um lead é criado automaticamente).
prefill{ name?, nationalId?, birthDate?, phone?, email? }Persiste dados no lead, preenchendo a próxima tela. phone e email a partir do SDK 0.40.0.
theme'light' | 'dark' | 'auto'Tema da interface.
closeOnCompletebooleanFecha a modal ao finalizar. Default: true em mode: 'modal', false inline. Com onDecision registrado, o fechamento automático espera a decisão (fechar antes derrubaria o iframe que a acompanha).
onStepfunctionCallback onStep(step, { progress, leadId }) a cada passo.
onProgressfunctionCallback onProgress(progress) com o progresso do funnel.
onCompletefunctionCallback onComplete({ leadId, status? }) ao concluir o fluxo. Dispara uma vez; status vem preenchido quando a decisão já é conhecida nesse momento.
onDecisionfunctionCallback onDecision({ leadId, status }) quando a decisão da verificação chega: approved, rejected ou manual. Veja Fim do fluxo.
onClosefunctionCallback onClose() quando a modal é dispensada pelo usuário.
onSessionfunctionCallback onSession({ leadId, externalId? }) assim que a sessão é criada, antes de o iframe renderizar. É como você obtém o leadId para encadear várias telas. O externalId vem preenchido quando o lead retomado já tinha uma referência sua. SDK 0.40.0+ (externalId em 0.41.0).
onErrorfunctionCallback onError({ error, status }) quando o embed não conseguiu montar (falha HTTP no bootstrap, erro de rede, ou seletor de mount inexistente). SDK 0.40.0+.

Posicionamento

Você pode embutir o funnel inline na página ou exibi-lo em uma modal.

Inline — informe mount com o seletor do elemento alvo (ou use mode: 'inline'). Por padrão o iframe faz auto-resize, acompanhando a altura do conteúdo; use height para fixar uma altura em px:

js
Zarv("smartflow", {
  publicKey: "pk-XXXXXXXX",
  mount: "#zarv-smartflow",
  // height: 640, // opcional; sem isso, auto-resize ao conteúdo
});

Modal — use mode: 'modal'. A modal é sempre centralizada e, por padrão, ajusta à altura do conteúdo da tela (auto-resize). Use modal para ajustar largura, altura e margem:

js
Zarv("smartflow", {
  publicKey: "pk-XXXXXXXX",
  mode: "modal",
  modal: { width: 480, margin: 24 }, // height fixa a altura, se informado
  onClose: () => console.log("modal fechada"),
});

modalSteps — mesmo embutido inline, você pode promover telas específicas (ex.: liveness / captura de documento) para uma modal overlay, listando os nomes das etapas:

js
Zarv("smartflow", {
  publicKey: "pk-XXXXXXXX",
  mount: "#zarv-smartflow",
  modalSteps: ["face", "cnh"], // essas etapas abrem em modal; as demais ficam inline
});

Aparência

O funnel aplica a identidade visual da campanha (logo, cores, alinhamento, fundo) na própria página — você não configura nada disso pelo SDK.

O que o SDK faz, a partir do SDK 0.41.0, é usar duas dessas configurações no enquadramento que ele desenha em volta do iframe:

  • bgColor pinta o card da modal e o overlay promovido. Sem isso, um funnel com tema escuro aparecia dentro de um card branco — branco nos cantos arredondados e um flash branco antes do primeiro paint.
  • buttonShape define o canto do botão de fechar, que é um botão como os outros da campanha.

Nada a fazer do seu lado: os valores vêm da campanha e são aplicados no mount.

Tela única (componentes isolados)

Use screen para embutir uma única tela em vez do fluxo inteiro:

js
Zarv("smartflow", {
  publicKey: "pk-XXXXXXXX",
  mount: "#zarv-smartflow",
  screen: "face",
  onComplete: ({ leadId }) => console.log("face concluída", leadId),
});

Valores possíveis: 'face', 'cnh', 'address', 'email', 'phone'. Omita screen para rodar o fluxo inteiro.

As telas são chamadas de forma independente — elas não seguem a ordem do fluxo. Ao concluir uma tela, o SDK dispara onComplete para que você trate o próximo passo do seu lado. A ordem do funnel é respeitada apenas pela versão hosted, não pelo SDK.

Várias telas no seu formulário

O caso mais comum de tela única é rodar várias delas como etapas do seu próprio cadastro — por exemplo, validar telefone, e-mail, facial e CNH dentro do formulário que você já tem.

Todas as telas precisam cair no mesmo lead. O encadeamento funciona assim:

  1. A primeira chamada vai sem leadId. O backend cria o lead e devolve o id em onSession({ leadId }).
  2. Você guarda esse leadId e o passa em todas as chamadas seguintes.
  3. No mesmo onSession, um identify amarra o seu identificador (externalId) ao lead — é por ele que o seu backend vai reconhecer o resultado depois.
js
const SEQUENCIA = ["phone", "email", "face", "cnh"];
let leadId = null;

function etapa(i) {
  if (i >= SEQUENCIA.length) return finalizar();
  const screen = SEQUENCIA[i];

  const config = {
    publicKey: "pk-XXXXXXXX",
    screen,
    mount: "#slot-" + screen,
    onSession: ({ leadId: id }) => {
      if (leadId) return;
      leadId = id;
      // Amarra a sua referência (pedido, reserva, matrícula…) ao lead.
      Zarv("smartflow", {
        action: "identify",
        leadId,
        externalId: "RES-00042",
        name: cadastro.nome,
        nationalId: cadastro.cpf,
        birthDate: cadastro.nascimento,
      });
    },
    onComplete: () => etapa(i + 1),
    onError: (err) => mostrarFallbackProprio(err),
  };

  if (leadId) config.leadId = leadId;
  else config.prefill = {
    name: cadastro.nome,
    nationalId: cadastro.cpf,
    birthDate: cadastro.nascimento,
    phone: cadastro.telefone,
    email: cadastro.email,
  };

  Zarv("smartflow", config);
}

function finalizar() {
  Zarv("smartflow", {
    action: "complete",
    leadId,
    metadata: { reserva: "RES-00042" },
    onComplete: () => aguardarDecisaoNoSeuBackend(),
    onError: (err) => tratarErro(err),
  });
}

etapa(0);

Pontos que costumam morder:

  • Passe leadId explicitamente em identify e complete quando houver mais de uma tela na página. Sem ele, essas chamadas miram a última sessão montada — o que raramente é a que você quer. (leadId nessas chamadas requer o SDK 0.40.0+.)
  • A ordem é sua. As telas isoladas não seguem a ordem do funnel: cada onComplete devolve o controle para você decidir a próxima.
  • onError não é opcional aqui. Um embed que falha no meio do seu formulário deixa um espaço em branco; sem onError a sua página não tem como perceber e cair num caminho alternativo.
  • A decisão pode não chegar no browser. Ela vem do polling de dentro do iframe; se o visitante fecha a página ou você desmonta o último embed antes de a verificação terminar, o onDecision simplesmente não dispara. Trate o webhook da campanha como o canal real — inclusive para confirmar cada etapa, via os eventos lead.phone.completed, lead.email.completed, lead.face.completed e lead.cnh.completed.

E a tela de dados básicos?

Ela não aparece quando você monta telas isoladas. O campo screen aceita apenas 'phone', 'email', 'face', 'cnh' e 'address' — não existe uma tela de nome/CPF/nascimento para embutir. Essa tela só existe no fluxo completo (sem screen).

Ou seja: se o seu cadastro já coletou esses dados, o visitante nunca os digita de novo. Mande-os na primeira chamada, por prefill, ou depois por identify:

js
// no mount da primeira tela
prefill: { name: "Ana", nationalId: "...", birthDate: "1990-01-02" }

// ou, em runtime, sobre um lead que já existe
Zarv("smartflow", {
  action: "identify",
  leadId,
  name: "Ana",
  nationalId: "...",
  birthDate: "1990-01-02",
});

O complete é o que cobra esses dados: ele responde 422 basic_data_incomplete enquanto o lead não tiver nome, CPF e nascimento. Se você receber esse erro mesmo tendo mandado prefill, é sinal de que algum dos três não chegou.

Fim do fluxo: onComplete e onDecision

Dois momentos distintos encerram um cadastro embutido:

  1. onComplete({ leadId, status? }) — o visitante chegou à tela final do funnel. A verificação (face, CNH, risco) pode ainda estar rodando; nesse caso status vem ausente. Dispara uma única vez.
  2. onDecision({ leadId, status }) — a decisão da verificação foi tomada: approved, rejected ou manual (revisão humana). Se a decisão já era conhecida quando o visitante chegou à tela final, dispara imediatamente após o onComplete; caso contrário, dispara assim que o backend decide (o funnel acompanha por polling e avisa o SDK).

Quem decide o que acontece no fim é você: redirecionar a própria página, fechar a modal, mostrar UI própria. Sem handler, o funnel mostra a tela de resultado dentro do iframe (ou redireciona para as URLs de resultado configuradas na campanha, quando existirem).

js
Zarv("smartflow", {
  publicKey: "pk-XXXXXXXX",
  mode: "modal",
  onDecision: ({ leadId, status }) => {
    if (status === "approved") location.href = "/bem-vindo";
    else if (status === "manual") mostrarBannerEmAnalise();
    else location.href = "/nao-aprovado";
  },
});

Notas:

  • Em mode: 'modal' com onDecision registrado, o closeOnComplete espera a decisão para fechar — fechar no onComplete derrubaria o iframe cujo polling é a fonte da decisão.
  • O onDecision é o sinal de UX no browser; o canal autoritativo server-side continua sendo o webhook da campanha (use-o para efeitos de negócio, não o callback).
  • Com o autopilot desligado na campanha, todo cadastro finaliza como manual — a aprovação/reprovação acontece depois, pela análise humana, e chega num segundo evento de webhook. No browser, o onDecision desses cadastros reporta manual: é o estado final que o visitante vê, não o desfecho.

Contexto do lead

Para continuar um cadastro já iniciado, passe leadId com um lead existente. Se você omitir leadId, o SDK inicia uma auto-sessão e cria um lead automaticamente.

O leadId criado chega no callback onSession, disparado assim que a sessão existe — antes de o iframe renderizar. É esse o momento de guardá-lo: os demais callbacks (onStep, onComplete) só chegam depois que o visitante interage, o que é tarde demais se você precisa montar a próxima tela no mesmo lead.

Prefill de identidade

Use prefill para persistir dados no lead, preenchendo a próxima tela:

js
Zarv("smartflow", {
  publicKey: "pk-XXXXXXXX",
  mount: "#zarv-smartflow",
  prefill: {
    name: "Ana",
    nationalId: "...",
    birthDate: "1990-01-02",
    phone: "+5511999998888", // SDK 0.40.0+
    email: "[email protected]", // SDK 0.40.0+
  },
});

phone e email importam sobretudo nas telas isoladas 'phone' e 'email': sem eles o visitante redigita um dado que você já tem, e pode digitar um valor diferente do que está no seu cadastro — quebrando a correspondência entre o que você guardou e o que foi verificado.

Prefill não pula a verificação: ele preenche o campo, então a tela de digitar é ignorada e o visitante cai direto na confirmação, que continua exigindo o código. Um telefone ou e-mail malformado é recusado na hora, com invalid_prefill_phone / invalid_prefill_email.

O prefill só faz sentido na primeira chamada de uma sessão. Depois que o lead existe, os dados já estão persistidos, e o backend nunca sobrescreve um campo já preenchido.

Identify em runtime

Depois de iniciar uma sessão (mesmo sem leadId), você pode identificar o usuário e anexar o seu próprio externalId:

js
Zarv("smartflow", {
  action: "identify",
  leadId, // opcional; obrigatório na prática se houver mais de uma tela na página
  externalId: "id-do-parceiro-123",
  name: "Ana",
  nationalId: "...",
  birthDate: "1990-01-02",
});

Essa chamada atualiza o lead: ela preenche apenas os campos que ainda estão vazios, enquanto o externalId é sempre setado. Assim, a próxima tela já vem preenchida com os dados informados.

Sem leadId, a chamada mira a sessão ativa — a última tela montada. Com uma única tela na página isso é o esperado; com várias, informe o leadId (obtido em onSession) para não acertar o lead errado. O campo leadId requer o SDK 0.40.0+.

Complete em runtime

Quando o seu fluxo termina, chame complete para finalizar o cadastro: ele anexa metadados que você coletou no seu lado e dispara a verificação do usuário na sessão ativa.

js
Zarv("smartflow", {
  action: "complete",
  leadId, // opcional; veja a nota sobre sessão ativa em Identify em runtime
  metadata: { pedido: "ABC-123", plano: "pro" }, // anexado em metadata.custom no lead
  onComplete: ({ leadId, status }) => {
    // verificação disparada (a decisão chega depois via webhook da campanha)
  },
  onError: (err) => {
    if (err.error === "basic_data_incomplete") {
      // o usuário ainda não informou os dados básicos (nome / CPF / nascimento)
    }
  },
});
CampoTipoDescrição
action'complete'Obrigatório.
leadIdstringLead alvo. Omitido, usa a sessão ativa (a última tela montada). SDK 0.40.0+.
metadataobjectMetadados adicionais do seu fluxo; persistidos sob metadata.custom no lead (vão também no webhook).
onComplete({ leadId, status }) => voidDisparado quando a verificação é acionada com sucesso.
onError({ error, status }) => voidDisparado em erro.

Comportamento:

  • Se o usuário não finalizou os dados básicos (IsBasicDataCompleted é falso), o complete retorna erro basic_data_incomplete (HTTP 422) no onError — nada é publicado.
  • A verificação é disparada conforme o estado do lead (ex.: após o face capture, a re-validação de liveness/face no ZarvID). É idempotente: um lead que já está em verificação/decidido não é re-enfileirado.
  • A decisão final (aprovado / em revisão / reprovado) não volta por este onComplete — ela chega de forma assíncrona pelo webhook da campanha e, no browser, pelo callback onDecision do mount.

Identificação do SDK

O SDK se identifica ao funnel com o parâmetro sdk=<versão> na URL do iframe (ex.: sdk=0.38.0). O funnel propaga esse parâmetro por toda a sessão e:

  • registra sdk_version nos metadados do lead (junto de utm_*/referrer), permitindo segmentar cadastros por superfície de aquisição (SDK embed vs. funnel hosted) e por versão do SDK;
  • marca os eventos de analytics do funnel com sdk_version;
  • ativa o modo embed automaticamente (não é preciso nenhum parâmetro adicional).

Nada a configurar do seu lado — o parâmetro é adicionado e transportado automaticamente.

Origens de embed (frame-ancestors)

Por padrão, o funnel permite ser embutido em js.zarv.com e js.zarv.dev (as origens oficiais do SDK) e nos domínios ativos do próprio flow.

Se um parceiro embutir o funnel em um host diferente do domínio do flow, é preciso adicionar essa origem em "AllowedEmbedOrigins" do flow (no admin). Sem isso, o navegador bloqueia o iframe via frame-ancestors.