Una API que se puede leer antes de firmar.
La especificación es pública, la firma de los webhooks puede verificarse en diez líneas de su lado. Esta página documenta lo que la API expone hoy — nada más, y es deliberado: lo que no está en producción no se documenta.
- Especificación OpenAPI legible sin clave ni cuenta
- Nunca una comunicación de operación sospechosa, nunca datos literales de clientes
- Webhooks firmados, verificables fuera de Vigilae
Servida por la propia API: el contrato que usted lee es el que se ejecuta.
El recorrido completo, y dos exclusiones que no se moverán.
Su software puede llevar un expediente de principio a fin: crear el cliente, abrir el expediente, aportar los documentos, lanzar el cribado, declarar los titulares reales, leer la valoración del riesgo y recuperar el expediente probatorio. Dos cosas no pasarán nunca por ahí, y no es una limitación del plan: ninguna comunicación de operación sospechosa y ningún dato literal de su cliente. La primera es un acto que la ley reserva a quien comunica; el segundo no tiene cabida en un canal de integración.
El expediente, de principio a fin
Clientes, expedientes, documentos justificativos, cribado, titulares reales, valoración del riesgo, expediente probatorio, control de integridad. Cada recurso tiene su scope: su clave solo abre lo que usted le ha dado.
Diario y agregados
Los eventos pKYC del despacho — documento caducado, cambio de titular real, recribado pendiente de revisión — y contadores de cartera para su panel. Respuesta siempre acotada, cursor estable.
Lo que usted escribe lleva un nombre
Aportar, cribar, calificar: cada escritura exige su scope y deja constancia del operador. Una calificación anónima se rechaza — la integración registra una decisión humana, no produce ninguna.
La API está pensada de servidor a servidor: una clave en código enviado al navegador es una clave publicada. Que la clave la custodie un proxy en su servidor, nunca la página.
Claves con scopes delimitados, mostradas una sola vez, revocables al instante.
- Formato. Toda clave empieza por
vgk_y se envía comoAuthorization: Bearer vgk_…. El secreto se muestra solo en la creación: no conservamos más que su huella SHA-256. - Scopes. Una clave solo lleva los scopes marcados en su creación: los generales
lecture(lectura, por defecto) yecriture(escritura), o scopes finos, recurso por recurso —clients:,dossiers:,pieces:,criblage:(enlectureoecriture),origine-fonds:lecture,journal:lecture.
La herencia es por sufijo y va en un solo sentido:lecturecubre todo*:lecture,ecrituretodo*:ecriture; un scope fino no abre nada más allá de sí mismo.
Seis scopes quedan fuera de ese par y, por tanto, no se heredan de ninguna clave general:alertes:qualifier,embed:emettre,adversemedia:consulter,conformite:consulter,dispositif:consulter,integrite:consulter. Se delegan uno a uno, al crear la clave.
Una clave sin el scope requerido recibe un 403 — véase #scopes. - Caducidad. Un año después de la creación (visible en la consola del despacho). Una clave caducada recibe un 401 explícito — véase #cle-expiree.
Una clave también puede dejar de aceptarse antes de esa fecha, cuando la API ya no forma parte de la suscripción del despacho. La parada llega entonces al menos 30 días después de un primer correo electrónico al despacho, en la fecha que indica un segundo correo, enviado al menos 7 días antes. En cuanto se fija, esa fecha puede leerse en la consola del despacho (campocoupure_ledeGET /connect/cles), y cada respuesta a una llamada aceptada la anuncia en la cabeceraSunsethasta que llega. Pasada esa fecha, la llamada recibe el 401 descrito en #authentification, con"motif": "fin_option_api"; si la API vuelve a la suscripción, la clave vuelve a aceptarse, hasta su propia fecha de caducidad.
Una clave revocada no vuelve con la API. Y cuando la suscripción entera se suspende o termina, las claves dejan de aceptarse en el acto, sin este plazo. - Límite de peticiones. 60 solicitudes por minuto y clave (ajustable clave por clave). Un 429 lleva
Retry-Aftery las cabecerasX-RateLimit-Limit / -Remaining / -Reset. - Revocación. Inmediata, desde la consola. La resolución se hace por huella: las claves no pueden enumerarse.
Tres llamadas curl y lo ha visto todo.
# Salud + autenticación de la clave
curl -s https://vigilae.org/api/v1/sante \
-H "Authorization: Bearer vgk_your_key"
# Diario de seguimiento, desde el principio, en páginas de 100
curl -s "https://vigilae.org/api/v1/evenements?depuisSeq=0&limite=100" \
-H "Authorization: Bearer vgk_your_key"
# Calificar el evento 42 (scope de escritura)
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 paginación, en una regla
Pase depuisSeq (0 en la primera llamada) y limite (con tope de 500): la respuesta viene ordenada por seq ascendente y devuelve prochainSeq, que se reenvía tal cual en la llamada siguiente. Una página más corta que limite significa el final del diario — prochainSeq se mantiene entonces estable y sirve de cursor de sondeo. Sin depuisSeq, obtiene la vista «lo más reciente primero», acotada a 500.
La cartera se consulta en /api/v1/portefeuille/agregats — contadores, una respuesta, sin paginación.
Firmados, con marca de tiempo, entregados al menos una vez.
En lugar de sondear el diario, recíbalo: Vigilae entrega los eventos en su URL, en orden, en lotes de 100 como máximo. La entrega es at-least-once — el cursor solo avanza con su 2xx, lote a lote — y la idempotencia va por seq: procese cada secuencia exactamente una vez (#idempotence).
Verificar la firma
Cada entrega lleva la cabecera x-vigilae-signature: t=<unix>,v1=HMAC-SHA256(secret, t + "." + body). Recalcule v1 a partir del cuerpo bruto recibido y rechace toda marca de tiempo t más allá de 5 min: ese es el anti-replay. Durante una rotación del secreto, la cabecera lleva un v1 por cada secreto aún válido (solapamiento de 24 h): acepte si cualquiera de ellos coincide.
// Node — verificación de la firma (sin dependencias)
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 — la misma verificación
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: el formato antiguo sha256=HMAC(body) se sigue emitiendo en x-vigilae-signature-legacy para los receptores ya desplegados; su retirada está prevista con los webhooks v2. Los receptores nuevos verifican el esquema con marca de tiempo de arriba, no el antiguo.
La rotación se lanza desde la consola (POST /connect/webhook/rotation del lado de la aplicación): durante 24 h el secreto antiguo sigue firmando — tiempo para desplegar el nuevo de su lado sin ventana de fallo.
El estado de vigilancia, en sus pantallas.
Su software ya crea los expedientes a través de la API; los widgets le dan la superficie que faltaba: mostrar el estado de vigilancia de un expediente dentro de su ficha de cliente, sin reconstruir una interfaz de cumplimiento. El flujo tiene tres pasos — su servidor emite un token de corta duración, la URL devuelta se coloca en un iframe, el widget muestra. Solo hace eso: ninguna decisión, ninguna calificación, ninguna escritura pasa nunca por un widget. El clic abre la aplicación Vigilae, donde actúa una persona identificada.
1. Emitir el token — del lado del servidor, nunca en la página
La clave vgk_ nunca toca el navegador. Su servidor emite el token con el scope dedicado embed:emettre (marcado al crear la clave, nunca heredado), y solo el token corto baja hasta la página: quince minutos por defecto, acotado entre 5 y 60, ligado a un expediente, un componente y un origen declarado. Viaja en el fragmento de la URL — no llega ni a nuestros registros ni a los suyos.
# Un token para el Compliance Button, etiqueta elegida del 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 el iframe — el lanzador, en diez líneas
El lanzador vigilae-embed.js crea el iframe en sandbox, realiza el handshake y sigue el tamaño del widget (acotado — el iframe nunca le impone su altura a su página). En cuanto a la CSP, su página abre una única directiva: frame-src https://vigilae.org. No exige script inline ni dependencia alguna. Un iframe escrito a mano funciona igual de bien.
<script src="https://vigilae.org/embarque/vigilae-embed.js" defer></script>
<div id="vigilance"></div>// En su script: la url viene de SU servidor, que emitió el token
var widget = VigilaeEmbed.monter({
conteneur: 'vigilance',
url: urlReturnedByTheMint,
renouvelerJeton: function () { // al expirar: reemitir del lado del servidor
return fetch('/my-server/vigilae-token').then(function (r) { return r.text(); });
},
});
// widget.rafraichir(token) envía un token nuevo; widget.demonter() retira todo.3. El Compliance Button: su etiqueta, nuestro estado
El botón elige la etiqueta de un catálogo cerrado — nunca texto libre en una superficie que habla de vigilancia. El shell solo la muestra en francés o en inglés (parámetro ?lang= de la URL del widget), todavía no en español; la lista siguiente da su significado:
controler_client— «Comprobar este cliente» (por defecto)lancer_vigilance— «Iniciar la diligencia debida»verifier_be— «Comprobar los titulares reales»preparer_dossier— «Preparar el expediente PBC/FT»
La pastilla de estado, en cambio, muestra un estado calculado por Vigilae: a_jour (verde — cribado y calificación al día), a_revoir (ámbar — cribado o calificación caducados, o la fuente falló en la última pasada), alerte (rojo — al menos una alerta por revisar), inconnu (gris — expediente nunca cribado). La etiqueta se configura, la semántica nunca: el color está ligado al estado dentro del propio shell, que ignora cualquier intento de sobrescribirlo por postMessage. Un expediente en alerta se muestra en alerta, sea cual sea el estilo aplicado.
El contrato postMessage, completo
El widget emite vigilae:pret, vigilae:taille (ajuste automático), vigilae:expire (reemita el token del lado del servidor y luego publique vigilae:jeton) y vigilae:ouvrir. Nunca un nombre, un riesgo o una alerta dentro de un mensaje: su software ya tiene todo por la API servidor a servidor, y este canal no se convertirá en un segundo canal.
frame-ancestors del shell. El administrador lo declara directamente en Vigilae, en la pantalla «Integraciones y seguridad»: diez orígenes https:// como máximo, host explícito, nunca un comodín; los componentes que activa; y una revocación masiva que anula, de una sola vez, todos los tokens ya emitidos. http://localhost solo se permite en un despacho de prueba — escriba a contact@vigilae.org (asunto «Widgets incrustados») para que le abran uno mientras dure su integración. Sin origen declarado: la emisión se rechaza con un 400. Una instancia cuyo almacén de orígenes no está conectado: un 501 explícito — véase #embed-indisponible.Sandbox: bajo petición.
Cada clase de error de la API tiene una dirección estable en la referencia de errores: #authentification, #scopes, #cloisonnement, #debit, #signature… Las respuestas de error de la API apuntarán a estas anclas.
La API no expone ni comunicaciones de operación sospechosa ni datos literales de clientes, por construcción (art. L.561-18 del CMF francés). Ninguna decisión de diligencia la toma la API: expone y califica eventos; el profesional decide.