> ## Documentation Index
> Fetch the complete documentation index at: https://legitimuz.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 6. Integrar

> Emita a sdkUrl no seu backend e monte o widget de verificação na sua página.

Ao final deste passo o fluxo de verificação abre dentro da sua página e os eventos da sessão chegam
ao seu código.

A integração tem duas metades, e a ordem importa: o seu **backend** cria a verificação e recebe a
`sdkUrl`; o seu **front-end** recebe essa `sdkUrl` e monta o widget. O navegador do titular nunca
toca na chave de API.

## Pré-requisitos

* Os passos [1](/setup/integration) a [5](/setup/webhook) concluídos: integração criada, fluxo
  **Publicado**, origem **Verificada**, chave de API com escopo de criar verificação e webhook
  cadastrado.

## Metade 1 — o seu backend emite a sdkUrl

<Info>
  A criação de verificações pela API ainda não está liberada. O contrato abaixo é o implementado no
  produto e não vai mudar de forma silenciosa; a liberação está descrita em
  [criar verificações](/api/verifications).
</Info>

O seu backend chama a porta de criação com a chave de API no header `x-api-key`:

```bash title="Criar a verificação" theme={null}
curl -X POST '<URL_BASE_DA_API>/public/verifications' \
  -H 'x-api-key: <SUA_CHAVE_DE_API>' \
  -H 'content-type: application/json' \
  -d '{
    "schema_version": "1.0",
    "document": { "type": "cpf", "number": "000.000.000-00" },
    "ref_id": "pedido-4471"
  }'
```

O corpo aceita apenas os campos abaixo, e recusa qualquer outro com `400`:

| Campo                          | Obrigatório | O que é                                                              |
| ------------------------------ | ----------- | -------------------------------------------------------------------- |
| `schema_version`               | sim         | sempre `"1.0"`                                                       |
| `document`                     | sim         | `{ "type": "cpf", "number": "..." }`. O CPF entra com ou sem máscara |
| `ref_id`                       | não         | o seu identificador do pedido. É a chave da idempotência             |
| `flow_public_id`               | não         | ausente usa o fluxo publicado padrão da integração                   |
| `integration_origin_public_id` | não         | ausente cria sem pino de origem                                      |

A resposta é `201` quando a verificação nasce e `200` quando o mesmo `ref_id` devolve a que já
existe:

```json title="Resposta" theme={null}
{
  "schema_version": "1.0",
  "verification": {
    "public_id": "<uuid>",
    "status": "pending",
    "expires_at": "2026-09-01T18:30:00.000Z"
  },
  "entry": {
    "kind": "web",
    "url": "https://verify.legitimuz.com/#v=<uuid>"
  }
}
```

**A `sdkUrl` é o `entry.url`.** Entregue esse valor ao seu front-end e nada mais.

<Warning>
  O `entry.url` carrega a credencial da verificação no fragmento (`#v=...`). Trate-a como segredo:
  não logue, não mande para tag manager, não persista. Ela vale por tempo limitado e para uma única
  verificação.
</Warning>

Origem nativa recebe `entry.kind: "native"` com o `public_id` direto, em vez da URL — o app o
entrega ao SDK nativo por canal seguro.

## Metade 2 — a sua página monta o widget

O Web SDK é carregado pelo CDN e fica disponível como `window.Legitimuz`:

```html theme={null}
<script src="https://sdk.legitimuz.com/v1/websdk.js"></script>
```

A URL `v1` é evergreen: serve sempre a última versão sem breaking change.

<Info>
  Os pacotes `@legitimuz/websdk` e `@legitimuz/websdk-react` publicam no npm no lançamento. Até lá,
  o CDN é a via disponível, e é a que os exemplos abaixo usam.
</Info>

O widget renderiza dentro de um elemento seu e ocupa 100% da altura dele. Sem altura, o widget
existe na página e não aparece.

O menor caminho completo, sem framework nenhum:

```html theme={null}
<div id="kyc-widget" style="height: 640px"></div>

<script src="https://sdk.legitimuz.com/v1/websdk.js"></script>
<script>
  const handle = window.Legitimuz.mount({
    sdkUrl: "<SUA_SDK_URL>",
    target: document.getElementById("kyc-widget"),
    onReady: () => console.info("widget pronto"),
    // "o fluxo terminou", não "deu certo": status é "submitted" ou "abandoned"
    onComplete: (result) => console.info("fim:", result.status),
    onError: (error) => console.error(error.code, error.user_message),
  });
</script>
```

`mount()` devolve um handle com `destroy()` e a promise `ready`. Chame `destroy()` ao remover o
widget da página: é o que encerra a câmera.

### Escolha o seu framework

Cada guia abaixo é completo e traz a armadilha própria daquele framework — o `<StrictMode>` no
React, a estratégia de carregamento no Next.js, o `ngAfterViewInit` no Angular.

<Columns cols={3}>
  <Card title="Vanilla JS" icon="js" href="/frameworks/vanilla-js">
    Uma tag `script` e nada mais, sem passo de build.
  </Card>

  <Card title="React" icon="react" href="/frameworks/react">
    Um hook que monta uma vez só, mesmo sob StrictMode.
  </Card>

  <Card title="Next.js" icon="triangle" href="/frameworks/next-js">
    App Router, com a página seguindo Server Component.
  </Card>

  <Card title="Vue" icon="vuejs" href="/frameworks/vue">
    Composable com `onMounted` e `onUnmounted`.
  </Card>

  <Card title="Angular" icon="angular" href="/frameworks/angular">
    Componente standalone com os callbacks como `@Output`.
  </Card>
</Columns>

<Tip>
  O SDK também expõe o custom element `<legitimuz-websdk>`, para quem preferir montar
  declarativamente. Só nesse caso o Vue precisa de `isCustomElement` e o Angular de
  `CUSTOM_ELEMENTS_SCHEMA` — pelo `mount()` dos exemplos acima, nenhum dos dois é necessário.
</Tip>

## Confira que funcionou

A primeira tela do fluxo aparece dentro do container e o `onReady` dispara. Se nada aparecer em 15
segundos, o `onError` recebe o código `6005`.

A lista completa de opções está em [opções do mount()](/web-sdk/options), o catálogo de eventos em
[eventos](/web-sdk/events) e os códigos em [tratamento de erros](/web-sdk/errors).

## Quando não funciona

<AccordionGroup>
  <Accordion title="Nada aparece e o onError recebe 6005">
    O widget não sinalizou `ready` dentro do tempo limite. As causas mais comuns:

    * a origem da sua página não está **Verificada** na integração — o navegador recusa o iframe
      sem avisar a sua página. Volte ao [passo 3](/setup/domains);
    * o fluxo não está **Publicado**. Volte ao [passo 2](/setup/flows);
    * a `sdkUrl` expirou. Emita outra.

    O campo `context.likelyCauses` do erro lista as hipóteses.
  </Accordion>

  <Accordion title="O onError recebe 6003 ou 6004">
    Falta `sdkUrl` ou `target` (`6003`), ou a `sdkUrl` não é a URL absoluta esperada (`6004`) —
    normalmente porque o backend entregou o objeto `entry` inteiro em vez do `entry.url`. O campo
    `context.param` aponta qual opção corrigir.
  </Accordion>

  <Accordion title="A chamada de criação responde 403">
    A chave de API não tem o escopo de criar verificação. Volte ao [passo 4](/setup/api-key) e
    confira **Escopo da chave**.
  </Accordion>
</AccordionGroup>

## Outros ambientes

<Columns cols={2}>
  <Card title="WebView iOS e Android" icon="smartphone" href="/web-sdk/webview">
    Abrir o fluxo dentro de uma WebView e liberar a câmera e o microfone.
  </Card>

  <Card title="Sem o Web SDK" icon="square-code" href="/web-sdk/iframe">
    Escrever o iframe você mesmo, e o que você deixa de receber ao fazer isso.
  </Card>
</Columns>

## Próximo passo

<Card title="7. Testar" icon="play" href="/setup/test">
  Rode a jornada ponta a ponta e veja a decisão chegar no seu webhook.
</Card>
