Un'API che puoi leggere prima di firmare.
La specifica è pubblica, la firma dei webhook si verifica in dieci righe dal tuo lato. Questa pagina documenta ciò che l'API espone oggi — niente di più, ed è una scelta: ciò che non è in produzione non è documentato.
- Specifica OpenAPI leggibile senza chiave né account
- Mai una segnalazione di operazione sospetta, mai il contenuto testuale dei clienti
- Webhook firmati, verificabili fuori da Vigilae
Servita dall'API stessa: il contratto che leggi è quello in esecuzione.
L'intero percorso, e due esclusioni che non cambieranno.
Il tuo software può portare una pratica dall'inizio alla fine: creare il cliente, aprire la pratica, depositare i documenti, avviare lo screening, dichiarare i titolari effettivi, leggere la classificazione del rischio e ritirare il fascicolo probatorio. Due cose non passeranno mai di qui, e non è un limite di piano: nessuna segnalazione di operazione sospetta e nessun contenuto testuale del tuo cliente. La prima è un atto che la legge riserva a chi effettua la segnalazione; il secondo non ha nulla da fare in un canale di integrazione.
La pratica, dall'inizio alla fine
Clienti, pratiche, documenti giustificativi, screening, titolari effettivi, classificazione del rischio, fascicolo probatorio, controllo di integrità. Ogni risorsa ha il suo scope: la tua chiave apre soltanto ciò che le hai dato.
Registro e aggregati
Gli eventi pKYC dello studio — documento scaduto, cambio di titolare effettivo, ri-screening da trattare — e i contatori di portafoglio per la tua dashboard. Risposta sempre limitata, cursore stabile.
Ciò che scrivi porta un nome
Depositare, sottoporre a screening, qualificare: ogni scrittura richiede il proprio scope e traccia l'operatore. Una qualificazione anonima viene rifiutata — l'integrazione registra una decisione umana, non ne produce nessuna.
L'API è pensata server-to-server: una chiave nel codice consegnato al browser è una chiave pubblicata. Fai custodire la chiave a un proxy lato server, mai alla pagina.
Chiavi con scope, mostrate una sola volta, revocabili all'istante.
- Formato. Ogni chiave inizia con
vgk_e si invia comeAuthorization: Bearer vgk_…. Il segreto è mostrato solo alla creazione: non ne conserviamo che l'impronta SHA-256. - Scope. Una chiave porta soltanto gli scope selezionati alla sua creazione: quelli generali
lecture(lettura, predefinito) eecriture(scrittura), oppure scope fini, risorsa per risorsa —clients:,dossiers:,pieces:,criblage:(inlectureoecriture),origine-fonds:lecture,journal:lecture.
L'ereditarietà segue il suffisso e va in un solo senso:lecturecopre ogni*:lecture,ecritureogni*:ecriture; uno scope fine non apre nulla al di là di sé stesso.
Sei scope escono da questa coppia e non si ereditano quindi da nessuna chiave generale:alertes:qualifier,embed:emettre,adversemedia:consulter,conformite:consulter,dispositif:consulter,integrite:consulter. Si delegano uno per uno, al momento della creazione della chiave.
Una chiave senza lo scope richiesto riceve un 403 — vedi #scopes. - Scadenza. Un anno dalla creazione (visibile nella console dello studio). Una chiave scaduta riceve un 401 esplicito — vedi #cle-expiree.
Una chiave può anche smettere di essere accettata prima di questa data, quando l'API non fa più parte dell'abbonamento dello studio. L'arresto cade allora almeno 30 giorni dopo una prima e-mail allo studio, alla data indicata da una seconda e-mail, inviata almeno 7 giorni prima. Una volta fissata, questa data si legge nella console dello studio (campocoupure_lediGET /connect/cles), e ogni risposta a una chiamata accettata la annuncia nell'intestazioneSunsetfinché non arriva. Dopo questa data, la chiamata riceve il 401 descritto in #authentification, con"motif": "fin_option_api"; se l'API rientra nell'abbonamento, la chiave torna a essere accettata, fino alla sua scadenza.
Una chiave revocata non torna con l'API. E quando l'intero abbonamento viene sospeso o termina, le chiavi smettono di essere accettate all'istante, senza attendere questo termine. - Frequenza. 60 richieste/minuto per chiave (modificabile per singola chiave). Un 429 porta
Retry-Aftere le intestazioniX-RateLimit-Limit / -Remaining / -Reset. - Revoca. Immediata, dalla console. La risoluzione avviene per impronta: le chiavi non si possono enumerare.
Tre chiamate curl e hai visto tutto.
# Stato di salute + autenticazione della chiave
curl -s https://vigilae.org/api/v1/sante \
-H "Authorization: Bearer vgk_your_key"
# Registro di monitoraggio, dall'inizio, a pagine da 100
curl -s "https://vigilae.org/api/v1/evenements?depuisSeq=0&limite=100" \
-H "Authorization: Bearer vgk_your_key"
# Qualificare l'evento 42 (scope di scrittura)
curl -s -X POST https://vigilae.org/api/v1/evenements/42/qualifier \
-H "Authorization: Bearer vgk_your_key" \
-H "Content-Type: application/json" \
-d '{"statut":"qualifie","disposition":"traite"}'La paginazione, in una regola
Passa depuisSeq (0 alla prima chiamata) e limite (con tetto a 500): la risposta è ordinata per seq crescente e restituisce prochainSeq, da ripassare tale e quale alla chiamata successiva. Una pagina più corta di limite significa fine del registro — prochainSeq resta allora stabile e funge da cursore di polling. Senza depuisSeq, ottieni la vista "più recenti prima", limitata a 500.
Il portafoglio si interroga su /api/v1/portefeuille/agregats — contatori, una risposta, nessuna paginazione.
Firmati, con marca temporale, consegnati almeno una volta.
Invece di interrogare il registro, ricevilo: Vigilae consegna gli eventi al tuo URL, in ordine, a lotti di al massimo 100. La consegna è at-least-once — il cursore avanza solo al tuo 2xx, lotto per lotto — e l'idempotenza passa dal seq: elabora ogni sequenza esattamente una volta (#idempotence).
Verificare la firma
Ogni consegna porta l'intestazione x-vigilae-signature: t=<unix>,v1=HMAC-SHA256(secret, t + "." + body). Ricalcola v1 dal corpo grezzo ricevuto e rifiuta ogni marca temporale t oltre i 5 min: è l'anti-replay. Durante una rotazione del segreto l'intestazione porta un v1 per ogni segreto ancora valido (sovrapposizione di 24 h): accetta se almeno uno corrisponde.
// Node — verifica della firma (nessuna dipendenza)
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 — stessa verifica
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 FalseTransitorio: il vecchio formato sha256=HMAC(body) è ancora emesso su x-vigilae-signature-legacy per i ricevitori già in essere; la sua rimozione è prevista con i webhook v2. I nuovi ricevitori verificano lo schema con marca temporale qui sopra, non il vecchio.
La rotazione si attiva dalla console (POST /connect/webhook/rotation lato applicazione): per 24 h il vecchio segreto continua a firmare — il tempo di distribuire il nuovo dal tuo lato senza finestra di guasto.
Lo stato di vigilanza, nei tuoi schermi.
Il tuo software crea già le pratiche tramite l'API; i widget le danno la superficie che mancava: mostrare lo stato di vigilanza di una pratica dentro la tua scheda cliente, senza ricostruire un'interfaccia di conformità. Il flusso si articola in tre passaggi — il tuo server emette un token di breve durata, l'URL restituita va in un'iframe, il widget lo mostra. Mostrare è tutto ciò che fa: nessuna decisione, nessuna qualificazione, nessuna scrittura passa mai da un widget. Il clic apre l'applicazione Vigilae, dove agisce una persona identificata.
1. Emettere il token — lato server, mai nella pagina
La chiave vgk_ non tocca mai il browser. Il tuo server chiama il mint con lo scope dedicato embed:emettre (spuntato alla creazione della chiave, mai ereditato) e solo il token corto scende fino alla pagina: quindici minuti di default, limitato a 5-60, legato a una pratica, un componente, un'origine dichiarata. Viaggia nel frammento dell'URL — non raggiunge né i nostri log né i tuoi.
# Un token per il Compliance Button, dicitura scelta dal catalogo
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. Posizionare l'iframe — il launcher, in dieci righe
Il launcher vigilae-embed.js crea l'iframe sandboxata, esegue l'handshake e segue le dimensioni del widget (limitate — l'iframe non impone mai la propria altezza alla tua pagina). Sul fronte CSP, la tua pagina apre una sola direttiva: frame-src https://vigilae.org. Nessuno script inline richiesto, nessuna dipendenza. Un'iframe scritta a mano funziona altrettanto bene.
<script src="https://vigilae.org/embarque/vigilae-embed.js" defer></script>
<div id="vigilance"></div>// Nel tuo script: l'url arriva dal TUO server, che ha chiamato il mint
var widget = VigilaeEmbed.monter({
conteneur: 'vigilance',
url: urlReturnedByTheMint,
renouvelerJeton: function () { // alla scadenza: ri-emetti lato server
return fetch('/my-server/vigilae-token').then(function (r) { return r.text(); });
},
});
// widget.rafraichir(token) invia un token nuovo; widget.demonter() rimuove tutto.3. Il Compliance Button: dicitura a scelta, stato non negoziabile
La dicitura del pulsante si sceglie da un catalogo chiuso — mai testo libero su una superficie che parla di vigilanza; lo shell la mostra solo in francese o in inglese (parametro ?lang=), non ancora nella tua lingua — l'elenco qui sotto ne dà il senso:
controler_client— "Controlla questo cliente" (predefinito)lancer_vigilance— "Avvia l'adeguata verifica"verifier_be— "Verifica i titolari effettivi"preparer_dossier— "Prepara la pratica antiriciclaggio"
Il pallino di stato, invece, rende uno stato calcolato da Vigilae: a_jour (verde — screening e classificazione aggiornati), a_revoir (ambra — screening o classificazione scaduti, oppure fonte in errore all'ultimo passaggio), alerte (rosso — almeno un'allerta da esaminare), inconnu (grigio — pratica mai sottoposta a screening). La dicitura si configura, la semantica mai: il colore è legato allo stato all'interno dello shell stesso, che ignora ogni tentativo di sovrascriverlo via postMessage. Una pratica in allerta mostra un'allerta, qualunque sia lo stile.
Il contratto postMessage, per intero
Il widget emette vigilae:pret, vigilae:taille (auto-ridimensionamento), vigilae:expire (ri-emetti lato server, poi invia vigilae:jeton) e vigilae:ouvrir. Mai un nome, un rischio o un'allerta dentro un messaggio: il tuo software ha già tutto tramite l'API server-to-server, e questo canale non ne diventerà un secondo.
frame-ancestors dello shell. L'amministratore la dichiara direttamente in Vigilae, nella schermata "Integrazioni e sicurezza": dieci origini https:// al massimo, host esplicito, mai un carattere jolly; i componenti che attiva; e una revoca in blocco che uccide tutti i token già emessi con un solo gesto. http://localhost è ammesso solo su uno studio di prova — scrivi a contact@vigilae.org (oggetto "Embedded widgets") per farne aprire uno per la durata della tua integrazione. Nessuna origine dichiarata: il mint rifiuta con un 400. Un'istanza il cui storage delle origini non è collegato: un 501 esplicito — vedi #embed-indisponible.Sandbox: su richiesta.
Ogni classe di errore dell'API ha un indirizzo stabile nel riferimento degli errori: #authentification, #scopes, #cloisonnement, #debit, #signature… Le risposte di errore dell'API punteranno a queste ancore.
L'API non espone né segnalazioni di operazione sospetta né contenuti testuali dei clienti, per costruzione (art. L.561-18 del CMF francese). Nessuna decisione di vigilanza è presa dall'API: espone e qualifica eventi; il professionista decide.