Vigilae Connect

Uma API que pode ler antes de assinar.

A especificação é pública, a assinatura dos webhooks verifica-se em dez linhas do seu lado. Esta página documenta o que a API expõe hoje — nada mais, e isso é deliberado: o que não está entregue não está documentado.

  • Especificação OpenAPI legível sem chave nem conta
  • Nunca uma comunicação de operação suspeita, nunca conteúdos literais de clientes
  • Webhooks assinados, verificáveis fora da Vigilae

Servida pela própria API: o contrato que lê é o que está a correr.

O perímetro

O percurso completo, e duas exclusões que não mudarão.

O seu software pode conduzir um processo de ponta a ponta: criar o cliente, abrir o processo, submeter os documentos, lançar a triagem, declarar os beneficiários efetivos, ler a classificação de risco e obter o processo de prova. Duas coisas nunca passarão por aqui, e não é uma limitação de plano: nenhuma comunicação de operação suspeita e nenhum conteúdo literal do seu cliente. A primeira é um ato que a lei reserva a quem comunica; o segundo nada tem que fazer num canal de integração.

O processo, de ponta a ponta

Clientes, processos, documentos comprovativos, triagem, beneficiários efetivos, classificação de risco, processo de prova, controlo de integridade. Cada recurso tem o seu scope: a sua chave só abre aquilo que lhe deu.

Diário e agregados

Os eventos pKYC do gabinete — documento caducado, alteração de beneficiário efetivo, nova triagem a tratar — e contadores de carteira para o seu painel. Resposta sempre limitada, cursor estável.

O que escreve tem um nome

Submeter, triar, qualificar: cada escrita exige o seu scope e deixa rasto do operador. Uma qualificação anónima é recusada — a integração regista uma decisão humana, não produz nenhuma.

A API foi concebida servidor-a-servidor: uma chave em código enviado para o navegador é uma chave publicada. Deixe a chave num proxy do lado do servidor, nunca na página.

Chaves

Chaves com scopes delimitados, mostradas uma única vez, revogáveis de imediato.

  • Formato. Cada chave começa por vgk_ e é enviada como Authorization: Bearer vgk_…. O segredo só é mostrado na criação: não conservamos senão o seu digest SHA-256.
  • Scopes. Uma chave só transporta os scopes assinalados na sua criação: os gerais lecture (leitura, por defeito) e ecriture (escrita), ou scopes finos, recurso a recurso — clients:, dossiers:, pieces:, criblage: (em lecture ou ecriture), origine-fonds:lecture, journal:lecture.
    A herança segue o sufixo e só vai num sentido: lecture cobre todos os *:lecture, ecriture todos os *:ecriture; um scope fino não abre nada para além de si próprio.
    Seis scopes saem deste par e, por isso, não se herdam de nenhuma chave geral: alertes:qualifier, embed:emettre, adversemedia:consulter, conformite:consulter, dispositif:consulter, integrite:consulter. Delegam-se um a um, na criação da chave.
    Uma chave sem o scope necessário recebe um 403 — ver #scopes.
  • Validade. Um ano após a criação (visível na consola do gabinete). Uma chave caducada recebe um 401 explícito — ver #cle-expiree.
    Uma chave pode também deixar de ser aceite antes dessa data, quando a API deixa de fazer parte da subscrição do gabinete. A paragem ocorre então pelo menos 30 dias depois de um primeiro e-mail ao gabinete, na data indicada por um segundo e-mail, enviado pelo menos 7 dias antes. Logo que fixada, essa data pode ler-se na consola do gabinete (campo coupure_le de GET /connect/cles), e cada resposta a uma chamada aceite anuncia-a no cabeçalho Sunset até que chegue. Passada essa data, a chamada recebe o 401 descrito em #authentification, com "motif": "fin_option_api"; se a API voltar à subscrição, a chave volta a ser aceite, até ao seu próprio prazo de validade.
    Uma chave revogada não volta com a API. E quando a subscrição inteira é suspensa ou termina, as chaves deixam de ser aceites de imediato, sem este prazo.
  • Débito. 60 pedidos/minuto por chave (ajustável por chave). Um 429 transporta Retry-After e os cabeçalhos X-RateLimit-Limit / -Remaining / -Reset.
  • Revogação. Imediata, a partir da consola. A resolução faz-se por digest: as chaves não podem ser enumeradas.
Início rápido

Três chamadas curl e viu tudo.

# Estado de saúde + autenticação da chave curl -s https://vigilae.org/api/v1/sante \ -H "Authorization: Bearer vgk_a_sua_chave" # Diário de monitorização, desde o início, em páginas de 100 curl -s "https://vigilae.org/api/v1/evenements?depuisSeq=0&limite=100" \ -H "Authorization: Bearer vgk_a_sua_chave" # Qualificar o evento 42 (scope de escrita) curl -s -X POST https://vigilae.org/api/v1/evenements/42/qualifier \ -H "Authorization: Bearer vgk_a_sua_chave" \ -H "Content-Type: application/json" \ -d '{"statut":"qualifie","disposition":"traite"}'

Paginação, numa só regra

Passe depuisSeq (0 na primeira chamada) e limite (com teto em 500): a resposta vem ordenada por seq ascendente e devolve prochainSeq, a repassar tal e qual na chamada seguinte. Uma página mais curta do que limite significa o fim do diário — prochainSeq mantém-se então estável e serve de cursor de polling. Sem depuisSeq, obtém a vista «mais recentes primeiro», limitada a 500.

A carteira consulta-se em /api/v1/portefeuille/agregats — contadores, uma resposta, sem paginação.

Webhooks

Assinados, com carimbo temporal, entregues pelo menos uma vez.

Em vez de fazer polling ao diário, receba-o: a Vigilae entrega os eventos no seu URL, por ordem, em lotes de 100 no máximo. A entrega é at-least-once — o cursor só avança com o seu 2xx, lote a lote — e a idempotência faz-se por seq: processe cada sequência exatamente uma vez (#idempotence).

Verificar a assinatura

Cada entrega transporta o cabeçalho x-vigilae-signature: t=<unix>,v1=HMAC-SHA256(secret, t + "." + body). Recalcule v1 a partir do corpo bruto recebido e rejeite qualquer carimbo t além de 5 min: é essa a proteção anti-replay. Durante uma rotação do segredo, o cabeçalho transporta um v1 por cada segredo ainda válido (sobreposição de 24 h): aceite se qualquer um deles corresponder.

// Node — verificação da assinatura (sem dependências) import { createHmac, timingSafeEqual } from 'node:crypto'; export function signatureValid(header, rawBody, secrets, toleranceMs = 5 * 60 * 1000) { const t = Number((/(?:^|,)t=(\d+)/.exec(header) || [])[1]); if (!t || Math.abs(Date.now() - t * 1000) > toleranceMs) return false; // anti-replay const received = [...header.matchAll(/v1=([0-9a-f]{64})/g)].map((m) => m[1]); return [].concat(secrets).some((secret) => { const expected = createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex'); return received.some((r) => r.length === expected.length && timingSafeEqual(Buffer.from(r), Buffer.from(expected))); }); }
# Python — a mesma verificação import hmac, hashlib, re, time def signature_valid(header: str, raw_body: bytes, secrets, tolerance_s: int = 300) -> bool: m = re.search(r"(?:^|,)t=(\d+)", header) if not m or abs(time.time() - int(m.group(1))) > tolerance_s: return False # anti-replay received = re.findall(r"v1=([0-9a-f]{64})", header) for secret in secrets: expected = hmac.new(secret.encode(), m.group(1).encode() + b"." + raw_body, hashlib.sha256).hexdigest() if any(hmac.compare_digest(expected, r) for r in received): return True return False

Transitório: o formato antigo sha256=HMAC(body) continua a ser emitido em x-vigilae-signature-legacy para os recetores já instalados; a sua remoção está prevista com os webhooks v2. Os recetores novos verificam o esquema com carimbo temporal acima, não o antigo.

A rotação dispara-se a partir da consola (POST /connect/webhook/rotation do lado da aplicação): durante 24 h o segredo antigo continua a assinar — tempo para instalar o novo do seu lado sem janela de falha.

Widgets integrados

O estado de vigilância, dentro dos seus ecrãs.

O seu software já cria os processos pela API; os widgets dão-lhe a superfície que faltava: mostrar o estado de vigilância de um processo dentro da sua ficha de cliente, sem reconstruir uma interface de conformidade. O fluxo tem três tempos — o seu servidor emite um token de curta duração, o URL devolvido entra numa iframe, o widget mostra. Mostrar é tudo o que faz: nenhuma decisão, nenhuma qualificação, nenhuma escrita passa alguma vez por um widget. O clique abre a aplicação Vigilae, onde um humano identificado atua.

1. Emitir o token — do lado do servidor, nunca na página

A chave vgk_ nunca toca no navegador. O seu servidor chama o mint com o scope dedicado embed:emettre (assinalado na criação da chave, nunca herdado) e só o token curto desce até à página: quinze minutos por defeito, limitado entre 5 e 60, ligado a um processo, um componente, uma origem declarada. Viaja no fragmento do URL — não chega nem aos nossos registos nem aos seus.

# Um token para o Compliance Button, rótulo escolhido no catálogo curl -s -X POST https://vigilae.org/api/v1/embed/jetons \ -H "Authorization: Bearer vgk_your_key" \ -H "Content-Type: application/json" \ -d '{"dossier_id":"D123","composant":"bouton", "audience":"https://your-software.example","libelle":"controler_client"}' # 201 -> { "jeton": "…", "expire_le": "…", # "url": "https://vigilae.org/embarque/bouton?c=…#…" }

2. Colocar a iframe — o lançador, em dez linhas

O lançador vigilae-embed.js cria a iframe em sandbox, faz o handshake e acompanha o tamanho do widget (limitado — a iframe nunca dita a altura à sua página). Do lado da CSP, a sua página abre uma única diretiva: frame-src https://vigilae.org. Nenhum script inline exigido, nenhuma dependência. Uma iframe escrita à mão funciona igualmente bem.

<script src="https://vigilae.org/embarque/vigilae-embed.js" defer></script> <div id="vigilance"></div>
// No seu script: o url vem do SEU servidor, que chamou o mint var widget = VigilaeEmbed.monter({ conteneur: 'vigilance', url: urlReturnedByTheMint, renouvelerJeton: function () { // ao expirar: reemitir do lado do servidor return fetch('/my-server/vigilae-token').then(function (r) { return r.text(); }); }, }); // widget.rafraichir(token) envia um token novo; widget.demonter() remove tudo.

3. O Compliance Button: o seu rótulo, o nosso estado

O rótulo do botão escolhe-se num catálogo fechado — nunca texto livre numa superfície que fala de vigilância — e o shell só o mostra em francês ou em inglês (parâmetro ?lang=), ainda não na sua língua; a lista abaixo dá o significado de cada um:

  • controler_client — «Controlar este cliente» (por defeito)
  • lancer_vigilance — «Iniciar a vigilância»
  • verifier_be — «Verificar beneficiários efetivos»
  • preparer_dossier — «Preparar o processo LCB-FT»

Já o indicador de estado mostra um estado calculado pela Vigilae: a_jour (verde — triagem e cotação em dia), a_revoir (âmbar — triagem ou cotação desatualizadas, ou falha da fonte na última execução), alerte (vermelho — pelo menos um alerta a tratar), inconnu (cinzento — processo nunca triado). O rótulo configura-se, a semântica nunca: a cor está ligada ao estado dentro do próprio shell, que ignora qualquer tentativa de a substituir por postMessage. Um processo em alerta mostra-se em alerta, seja qual for o estilo.

O contrato postMessage, na íntegra

O widget emite vigilae:pret, vigilae:taille (auto-ajuste), vigilae:expire (reemita do lado do servidor e depois publique vigilae:jeton) e vigilae:ouvrir. Nunca um nome, um risco ou um alerta dentro de uma mensagem: o seu software já tem tudo pela API server-to-server, e este canal não se tornará um segundo.

Declaração de origens: pelo administrador do gabinete. Um token de embed está ligado a uma origem declarada do seu gabinete — é ela que pilota a CSP frame-ancestors do shell. O administrador declara-a diretamente na Vigilae, no ecrã «Integrações e segurança»: dez origens https:// no máximo, anfitrião explícito, nunca um wildcard; os componentes que ativa; e uma revogação em massa que mata, num só gesto, todos os tokens já emitidos. http://localhost só é permitido num gabinete sandbox — escreva para contact@vigilae.org (assunto «Widgets integrados») para que se abra um pela duração da sua integração. Nenhuma origem declarada: o mint recusa com um 400. Uma instância cujo armazenamento de origens não está ligado: um 501 explícito — ver #embed-indisponible.
Ambiente de teste

Sandbox: a pedido.

Ainda não existe sandbox em self-service — preferimos dizê-lo aqui a deixá-lo descobrir. Escreva para contact@vigilae.org (assunto «Acesso API»): abrimos um gabinete de ensaio com dados fictícios pela duração da sua integração, e ficamos contactáveis enquanto ela avança.

Cada classe de erro da API tem um endereço estável na referência de erros: #authentification, #scopes, #cloisonnement, #debit, #signature… As respostas de erro da API apontarão para estas âncoras.

A API não expõe comunicações de operações suspeitas nem conteúdos literais de clientes, por construção (art. L.561-18 do CMF francês). Nenhuma decisão de vigilância é tomada pela API: expõe e qualifica eventos; o profissional decide.