> ## 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.

# Várias integrações no mesmo backend

> Um servidor atendendo mais de um canal, cada um com a sua chave, o seu fluxo e o seu webhook.

Quem tem app e site, ou mais de uma marca, tem mais de uma integração. O backend é um só; a chave,
o fluxo e o segredo do webhook não.

|               |                                                                          |
| ------------- | ------------------------------------------------------------------------ |
| **Stack**     | Node 20 · qualquer framework HTTP                                        |
| **Você terá** | uma função de criação que resolve o canal antes de escolher a credencial |

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

## Por que não uma chave só

Uma chave por canal é o que faz um vazamento no app não alcançar o site, e o webhook de
homologação não disparar no endpoint de produção. É também o que deixa você revogar a chave de um
canal sem derrubar o outro.

## O registro de canais

```ts title="lib/channels.ts" theme={null}
/* Um canal é uma integração da Legitimuz. As três credenciais andam JUNTAS: trocar a chave sem
   trocar o segredo do webhook é o erro que só aparece na primeira entrega. */
export interface Channel {
  apiKey: string;
  flowId: string;
  webhookSecret: string;
}

export const CHANNELS: Record<string, Channel> = {
  site: {
    apiKey: process.env.LEGITIMUZ_API_KEY_SITE!,
    flowId: process.env.LEGITIMUZ_FLOW_ID_SITE!,
    webhookSecret: process.env.LEGITIMUZ_WEBHOOK_SECRET_SITE!,
  },
  app: {
    apiKey: process.env.LEGITIMUZ_API_KEY_APP!,
    flowId: process.env.LEGITIMUZ_FLOW_ID_APP!,
    webhookSecret: process.env.LEGITIMUZ_WEBHOOK_SECRET_APP!,
  },
};

export function channelFor(name: string): Channel {
  const channel = CHANNELS[name];
  // Falha cedo e alto: canal desconhecido com fallback silencioso cria verificação na conta errada.
  if (!channel) throw new Error(`unknown channel: ${name}`);

  return channel;
}
```

## Criar no canal certo

```ts title="app/api/verifications/route.ts" theme={null}
import { channelFor } from "@/lib/channels";

export async function POST(request: Request) {
  const user = await authenticate(request);
  const registration = await db.registrations.byUser(user.id);

  // O canal vem do SEU contexto — a origem do cadastro, não um campo que o cliente manda.
  const channel = channelFor(registration.channel);

  const response = await fetch("https://api.legitimuz.com/public/verifications", {
    method: "POST",
    headers: { "X-API-Key": channel.apiKey, "Content-Type": "application/json" },
    body: JSON.stringify({
      schema_version: "1.0",
      ref_id: registration.id,
      document: { type: "cpf", number: registration.cpf },
      flow_public_id: channel.flowId,
    }),
  });

  const { verification, entry } = await response.json();
  await db.registrations.link(registration.id, verification.public_id, registration.channel);

  return Response.json({ entry });
}
```

<Warning>
  Nunca deixe o cliente escolher o canal pelo corpo da requisição. Quem manda o canal manda a chave
  usada, e com isso escolhe em qual conta a verificação nasce.
</Warning>

## Um endpoint de webhook por canal

A rota carrega o canal no caminho, e cada uma confere contra o seu próprio segredo:

```ts title="app/api/webhooks/[channel]/route.ts" theme={null}
export async function POST(request: Request, { params }: { params: { channel: string } }) {
  const channel = channelFor(params.channel);
  const rawBody = await request.text();

  if (!isSignatureValid(rawBody, request.headers.get("x-legitimuz-signature") ?? "", channel.webhookSecret)) {
    return new Response(null, { status: 401 });
  }

  const isNew = await db.deliveries.recordIfNew(request.headers.get("x-legitimuz-delivery")!);
  if (isNew) await queue.publish({ channel: params.channel, event: JSON.parse(rawBody) });

  return new Response(null, { status: 200 });
}
```

No dashboard, cada integração aponta o webhook dela para o seu caminho:
`https://seu-dominio/api/webhooks/site` e `https://seu-dominio/api/webhooks/app`.

<Info>
  Um endpoint por canal e não um endpoint com vários segredos: tentar cada segredo até um bater
  transforma uma falha de assinatura em algo indistinguível de um canal mal configurado.
</Info>

## 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.

Nesta POC cada variável ganha o sufixo do canal:

```bash title=".env" theme={null}
LEGITIMUZ_API_KEY_SITE=<SUA_CHAVE>
LEGITIMUZ_FLOW_ID_SITE=<FLOW_PUBLIC_ID>
LEGITIMUZ_WEBHOOK_SECRET_SITE=<SEGREDO_DO_ENDPOINT>

LEGITIMUZ_API_KEY_APP=<SUA_CHAVE>
LEGITIMUZ_FLOW_ID_APP=<FLOW_PUBLIC_ID>
LEGITIMUZ_WEBHOOK_SECRET_APP=<SEGREDO_DO_ENDPOINT>
```

## 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>

* Canais fixos em código. Com muitos canais, isso vira tabela e cache.
* Sem rotação de chave por canal sem downtime.
* Sem métrica separada por canal.

## Próximo passo

<Columns cols={2}>
  <Card title="Integrações" icon="puzzle" href="/platform/integrations">
    Ambientes, origens, chaves e aparência.
  </Card>

  <Card title="Cadastrar webhooks" icon="webhook" href="/platform/webhooks">
    Escopo por integração e por conta.
  </Card>
</Columns>
