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

# Passar do desktop para o celular

> O titular está no computador e a verificação precisa da câmera do celular: entregue a mesma jornada por QR code.

Boa parte dos cadastros começa no desktop, e webcam de notebook rende captura pior que a câmera
traseira de um celular. A `entry.url` resolve isso sem chamada nova: é a mesma jornada, aberta em
outro aparelho.

|               |                                                                        |
| ------------- | ---------------------------------------------------------------------- |
| **Stack**     | Next.js 15 · `qrcode`                                                  |
| **Você terá** | um QR code na tela do desktop e o desfecho chegando pelo mesmo webhook |

<Card title="Confira o exemplo" icon="brand-github" href="https://github.com/Legitimuz-Tech/legitimuz-examples/tree/master/pocs/qr-handoff" horizontal>
  Os arquivos desta POC, com o destino de cada um.
</Card>

## Gerar o QR

A `entry.url` já é a jornada completa. Transformá-la em QR é tudo o que falta.

```ts title="app/api/verifications/route.ts" theme={null}
import QRCode from "qrcode";

export async function POST() {
  const { verification, entry } = await createVerification();

  if (entry.kind !== "web") {
    return Response.json({ error: "entry_not_web" }, { status: 409 });
  }

  // O QR é gerado no SERVIDOR. A URL não passa pelo estado do cliente nem por analytics.
  const qr = await QRCode.toDataURL(entry.url, { margin: 1, width: 320 });

  return Response.json({ qr, expiresAt: verification.expires_at });
}
```

<Warning>
  A `entry.url` carrega a credencial da jornada. Não a coloque em `<a href>`, em parâmetro de
  query, em log ou em ferramenta de analytics: o QR é a única superfície onde ela deve aparecer, e
  para o titular daquela verificação.
</Warning>

## Mostrar e esperar

```tsx title="app/registration/qr.tsx" theme={null}
"use client";

import { useEffect, useState } from "react";

export function VerificationQr({ qr, expiresAt }: { qr: string; expiresAt: string }) {
  const [status, setStatus] = useState<"waiting" | "done" | "expired">("waiting");

  useEffect(() => {
    // O desktop não sabe o que acontece no celular. Quem sabe é o seu backend, pelo webhook —
    // aqui só perguntamos a ele, nunca à Legitimuz.
    const timer = setInterval(async () => {
      const { outcome } = await fetch("/api/verifications/state").then((r) => r.json());
      if (outcome) setStatus("done");
      if (new Date(expiresAt) < new Date()) setStatus("expired");
    }, 3_000);

    return () => clearInterval(timer);
  }, [expiresAt]);

  if (status === "expired") return <ExpiredWithRestartButton />;
  if (status === "done") return <Done />;

  return (
    <div className="flex flex-col items-center gap-3">
      <img src={qr} alt="QR code para abrir a verificação no celular" width={320} height={320} />
      <p>Aponte a câmera do seu celular para continuar.</p>
    </div>
  );
}
```

## O estado vem do seu banco

A rota que o desktop consulta lê o que o **seu** webhook já gravou. Não chame a Legitimuz num
intervalo: além de bater no teto da chave, o desfecho confiável é o que você persistiu.

```ts title="app/api/verifications/state/route.ts" theme={null}
export async function GET(request: Request) {
  const user = await authenticate(request);
  const registration = await db.registrations.byUser(user.id);

  return Response.json({ outcome: registration.outcome ?? null });
}
```

## Antes de rodar

```bash title=".env" theme={null}
LEGITIMUZ_API_KEY=<SUA_CHAVE>
LEGITIMUZ_WEBHOOK_SECRET=<SEGREDO_DO_ENDPOINT>
LEGITIMUZ_FLOW_ID=<FLOW_PUBLIC_ID>
```

| Variável                   | Onde achar                                                                       |
| -------------------------- | -------------------------------------------------------------------------------- |
| `LEGITIMUZ_API_KEY`        | Integrações → Segurança → [Chaves de API](/platform/tokens). Aparece uma vez     |
| `LEGITIMUZ_WEBHOOK_SECRET` | Integrações → Segurança → [Webhooks](/platform/webhooks), na criação do endpoint |
| `LEGITIMUZ_FLOW_ID`        | Solução KYC → Fluxos, no menu da linha, em **Copiar ID do fluxo**                |

Use uma integração **sandbox**. Nenhum dos três valores vai para o browser ou para o app.

## O que esta POC não faz

<Warning>
  POC é código para entender o fluxo, não para copiar em produção. Em todas elas, `authenticate()` é
  um stub, `db` é um objeto de mentira e não há migration, observabilidade nem retentativa própria.
</Warning>

* Polling simples de 3 segundos. Em produção, SSE ou WebSocket poupa requisição.
* Sem botão de "reenviar por SMS".
* Sem tratamento de titular que abre o QR duas vezes em aparelhos diferentes.

## Próximo passo

<Columns cols={2}>
  <Card title="Retomada e expiração" icon="clock" href="/guides/pocs/resume-expiry">
    O que fazer quando a jornada expira antes de terminar.
  </Card>

  <Card title="WhatsApp" icon="brand-whatsapp" href="/guides/pocs/whatsapp-handoff">
    A mesma entrega, quando o titular não está na sua tela.
  </Card>
</Columns>
