Documentación para integradores
Instala Biscotti y conecta tus integraciones con la elección del visitante. Referencia del API v1 desplegado.
Instalación HTML
Añade el host exacto, instala el código, verifica la instalación y publica la configuración. Copia el código de Instalación. Sustituye YOUR_SITE_KEY por la clave pública y usa el origen de tu plataforma: https://biscotti.dev en producción o https://dev.biscotti.dev en dev. La clave no da acceso administrativo. Instala un solo SDK en head, antes de las integraciones. Usa una sola modalidad por documento. Los scripts activos pueden ejecutarse antes del SDK diferido: conviértelos en inertes. Mantén un acceso permanente a los ajustes en el pie.
Actualizaciones automáticas (recomendado)
Pega el código una vez. Recibirás las correcciones del script automáticamente, sin sustituir el código a mano. Las instalaciones existentes y WordPress funcionan igual.
<script id="biscotti-sdk" src="https://biscotti.dev/consent.js" data-site-key="YOUR_SITE_KEY" defer></script>Versión fijada con SRI (avanzado)
Eliges una versión concreta del script. No recibirás correcciones automáticas, incluidas las de seguridad. Para actualizarla debes sustituir el código a mano. En ambos modos, los ajustes publicados del banner se obtienen automáticamente.
<script id="biscotti-sdk" src="https://biscotti.dev/sdk/9c2bc03275827be7431adcc709a295afb1f7778553dbaf3c3c4f8a2df233f702/consent.js" integrity="sha384-VtJRmbHcw1tU2ZG0b1SkNpDBvF0Wk2fuFyZG/CNH7stj+z9FgY8pbYK6eyk5KqmD" crossorigin="anonymous" data-site-key="YOUR_SITE_KEY" defer></script>WordPress
Descarga el ZIP, instala y activa el plugin. En Biscotti → Instalación → WordPress, copia la URL de plataforma y la clave pública al plugin. Un administrador genera el código temporal de emparejamiento de un solo uso. No publiques códigos ni credenciales en HTML. Usa el host exacto, publica y elimina instalaciones duplicadas. Emparejar o importar no publica el borrador ni bloquea automáticamente los demás plugins. Verifica los scripts reales del tema y los plugins.
Descargar · WordPressGoogle Tag Manager — Basic
Instala Biscotti fuera del contenedor. Elimina el código GTM estándar y su iframe noscript. Coloca el cargador debajo del SDK y sustituye GTM-XXXXXXX. Espera al consentimiento analítico; los contenedores mixtos necesitan controles por etiqueta. Pega la plantilla descargada en Código de una plantilla nativa, no en Custom HTML. Permite escritura access_consent para las siete señales, ejecución access_globals de Biscotti.onConsentChange, Biscotti.canActivate y dataLayer.push, y lectura/escritura de dataLayer. Usa Consent Initialization – All Pages y consentimientos adicionales por etiqueta. Verifica Preview / Tag Assistant. biscotti_consent_update de dataLayer es distinto del evento DOM biscotti:consentchange. Las reglas publicadas necesitan una condición canActivate positiva. La plantilla no está en Community Template Gallery.
<script type="text/plain" data-consent-category="analytics">
(function () {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ 'gtm.start': Date.now(), event: 'gtm.js' });
var script = document.createElement('script');
script.async = true;
script.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX';
document.head.appendChild(script);
})();
</script>Descargar · GTMAPI y disponibilidad
La presencia de window.Biscotti o load no significa disponibilidad. La configuración empieza a cargarse antes de DOMContentLoaded; la interfaz espera al DOM. Ready indica publicación validada, preferencias resueltas, interfaz, señales y escuchas entre pestañas preparadas. No decidir o rechazar son estados válidos. No espera auditoría, visitas, fuentes ni etiquetas externas. Los adaptadores restaurados se ejecutan después de los eventos iniciales; los callbacks Promise pueden llegar después de adaptadores síncronos. ready/error son estados finales de inicialización. Un error posterior no cambia ready. Tras un fallo inicial, las nuevas elecciones siguen emitiendo cambios, pero whenReady queda rechazado.
| Método | Comportamiento |
|---|---|
apiVersion | Constante 1: versión del contrato. |
openPreferences() | Abre ajustes; en loading encola la petición, sin conceder consentimiento. |
getConsent() | Copia modificable de seis booleanos; necessary siempre true. |
canActivate(ruleId) | Condición positiva de una regla publicada; desconocida, pausada o no disponible devuelve false. |
onConsentChange(callback) | Callback inmediato con copia y tras aplicar preferencias; puede repetirse. Devuelve unsubscribe y aísla excepciones. |
getStatus() | loading, ready o error de inicialización, no la decisión. |
whenReady() | Promise del snapshot inicial congelado, también para oyentes tardíos; rechaza si falla el inicio. |
getLastError() | Último error congelado o null; una elección correcta no borra el historial. |
Eventos DOM y datos
Escucha en window antes del SDK. ready se emite una vez; consentchange añade reason y error añade code/phase al snapshot. Son CustomEvent sin propagación ni cancelación. Los oyentes tardíos usan whenReady/getConsent/getLastError. Inicio: estado/GCM/interfaz → callbacks → ready → consentchange inicial → adaptadores. Cambio: estado → callbacks → consentchange → activación o recarga. Fallo: rechazo opcional → consentchange si cambia → error; sin ready si falla la inicialización. Los datos y consents están congelados y no incluyen identificadores de decisión, cookies, URL del visitante ni excepciones. hasDecision distingue rechazo explícito de ausencia de elección; los metadatos son null antes de validar. Las excepciones del oyente no detienen la aplicación del consentimiento; confirmar auditoría no duplica eventos.
W17 — biscotti:beforereload precede a reload/replace cuando se requiere tras retirar el consentimiento. BeforeReloadDetail = SdkSnapshot + reason + navigation. El código síncrono del listener en window se ejecuta antes de navegar. El evento no permite cancelar la navegación; el SDK no espera operaciones asíncronas. Consulta el orden y el ejemplo en la sección de retirada.
window.addEventListener('biscotti:ready', ({ detail }) => {
document.documentElement.dataset.biscottiReady = String(detail.apiVersion);
});
window.addEventListener('biscotti:consentchange', ({ detail }) => {
document.documentElement.dataset.analyticsConsent = String(detail.consents.analytics);
});
window.addEventListener('biscotti:error', ({ detail }) => {
console.warn('Biscotti', detail.code, detail.phase);
});type SdkSnapshot = {
readonly apiVersion: 1;
readonly siteKey: string | null;
readonly publicationVersion: number | null;
readonly consentEpoch: number | null;
readonly environment?: 'development';
readonly hasDecision: boolean;
readonly consents: Readonly<{
necessary: true;
functional: boolean; analytics: boolean; performance: boolean;
advertising: boolean; unclassified: boolean;
}>;
};
type ConsentChangeDetail = SdkSnapshot & {
readonly reason: 'initial' | 'choice' | 'storage' | 'expiry' | 'error';
};
type BeforeReloadDetail = SdkSnapshot & {
readonly reason: ConsentChangeDetail['reason'];
readonly navigation: 'reload' | 'replace';
};
type SdkErrorDetail = SdkSnapshot & {
readonly code:
| 'INVALID_SITE_KEY' | 'CONFIGURATION_UNAVAILABLE'
| 'INVALID_CONFIGURATION' | 'STORAGE_UNAVAILABLE'
| 'CONSENT_MODE_FAILED' | 'ADAPTER_FAILED'
| 'SYNC_FAILED' | 'INITIALIZATION_FAILED';
readonly phase: 'initialization' | 'runtime';
};Aplicaciones SPA
Ejecuta el ejemplo cuando el elemento script del SDK ya esté en el DOM, antes o después de terminar su descarga. Instala una vez en la estructura del documento. Suscríbete aunque falle whenReady. El ejemplo actualiza un atributo DOM; sustituye esa línea por tu integración y llama a disconnectBiscotti al desmontar. whenReady conserva el snapshot inicial: para el estado actual usa getConsent o la suscripción. La navegación SPA no reinicializa ni obtiene otra publicación. Los nuevos elementos inertes pueden ejecutarse una vez por elemento. No sustituyas window.Biscotti con una cola o una API provisional.
function connectBiscotti() {
const api = window.Biscotti;
if (!api || api.apiVersion !== 1) return () => {};
const unsubscribe = api.onConsentChange(consents => {
document.documentElement.dataset.analyticsConsent = String(consents.analytics);
});
void api.whenReady().catch(error => {
console.warn('Biscotti initialization', error.code);
});
return unsubscribe;
}
const script = document.getElementById('biscotti-sdk');
let disconnect = () => {};
const onLoad = () => { disconnect(); disconnect = connectBiscotti(); };
if (window.Biscotti) onLoad();
else script?.addEventListener('load', onLoad, { once: true });
const onError = () => { document.documentElement.dataset.analyticsConsent = 'false'; };
script?.addEventListener('error', onError);
function disconnectBiscotti() {
script?.removeEventListener('load', onLoad);
script?.removeEventListener('error', onError);
disconnect();
}Adaptadores de scripts inertes
Usa type="text/plain", data-consent-category y data-consent-src en lugar de src. Sustituye la URL de ejemplo. Las categorías opcionales empiezan rechazadas; cada elemento se activa una vez por documento tras el consentimiento. Solo scripts clásicos inline o HTTP(S); se copian nonce, integrity y crossorigin. Módulos, nomodule, iframe noscript, píxeles arbitrarios y cookies del servidor quedan fuera. El adaptador de categoría no depende de la pausa de reglas; canActivate comprueba la regla publicada sin ejecutar etiquetas. Revisa las etiquetas con varias finalidades.
<script type="text/plain" data-consent-category="analytics">
window.exampleAnalyticsStarted = true;
</script>
<script type="text/plain" data-consent-category="analytics"
data-consent-src="https://analytics.example.com/sdk.js"></script>Categorías y Google Consent Mode
Las señales opcionales empiezan en denied y security_storage en granted; se actualizan según la tabla. Es Basic, sin instalar automáticamente GA/GTM ni habilitar Advanced. performance y unclassified no tienen señal Google propia: aplica su categoría o una regla publicada positiva. La ausencia de señal no permite ejecutar una etiqueta. Las categorías deshabilitadas no se conceden. GTM requiere también los consentimientos adicionales correspondientes.
performance cubre mediciones técnicas: velocidad de carga y estabilidad del sitio. Clasifica la medición del tráfico, el comportamiento, GA4 y Clarity como analytics; la medición de campañas y el remarketing como advertising, aunque también midan velocidad. Clasifica por finalidad real; separa las herramientas con varias finalidades o exige todos los consentimientos correspondientes. performance y unclassified no tienen señal nativa de Google. performance por sí sola no concede analytics_storage ni consentimientos publicitarios. Exige esa categoría o una condición positiva de la regla publicada; la ausencia de señal no es permiso. En GTM exige biscottiPerformance igual a true y, para reglas publicadas, su condición positiva. No inventes performance_storage. El significado de las decisiones guardadas no cambia; esta corrección no exige otra decisión. Si cambias la finalidad o el alcance, publica renovando los consentimientos.
| Categoría | Comportamiento | Google Consent Mode |
|---|---|---|
necessary | Funciones básicas, seguridad y conservación de tu decisión. Siempre activas. | security_storage = granted |
functional | Funciones adicionales y conservación de las preferencias del visitante. | functionality_storage, personalization_storage |
analytics | Medición del tráfico y comprensión del uso del sitio. | analytics_storage |
advertising | Personalización de anuncios y medición de la eficacia de las campañas. | ad_storage, ad_user_data, ad_personalization |
performance | Medición de la velocidad, estabilidad y rendimiento del sitio. | Sin señal nativa de Google |
unclassified | Otras cookies cuya finalidad aún debe determinarse. | Sin señal nativa de Google |
Dominios y pruebas
Cada publicación corresponde a un hostname exacto: example.com, www.example.com y shop.example.com son distintos. El SDK y CORS comprueban ese host; la clave no lo evita. Las preferencias son host-only, sin compartir subdominios automáticamente. Dev y producción tienen datos separados. No existe excepción localhost ni staging vinculado en una licencia. Playground no tiene permisos especiales; localhost puede fallar la verificación en una plataforma alojada. Usa un host de pruebas accesible con publicación verificada. Otra publicación se obtiene en un documento nuevo.
Clave independiente para pruebas en localhost y staging. Los datos de prueba no se incluyen en informes de producción ni facturación. Guarda el borrador y aprueba la información de cookies primero. La copia usa esos datos y las últimas reglas publicadas (ninguna antes de publicar). Cada guardado renueva el consentimiento de prueba. Localhost, 127.0.0.1 y [::1] requieren el puerto exacto. Staging requiere HTTPS en el puerto 443 y verificar la instalación con la clave de prueba. El host de producción está excluido.
En el panel elige Instalación → Modo de desarrollo → Guardar configuración de prueba, añade http://localhost:5173 y copia el código dev_. Ejecuta el ejemplo en ese origen usando la plataforma Biscotti alojada, sin instalarla localmente. El código site_ y los datos de producción siguen separados. Para staging añade el origen HTTPS exacto, instala el código de prueba en su página principal y comprueba la instalación. La verificación no accede a direcciones privadas.
Las preferencias y la cola se separan por clave y origen, incluido el puerto. Los eventos y snapshots añaden environment: development; el banner indica que es una prueba. Desactivar, eliminar el origen o renovar la clave bloquea solicitudes futuras; recarga documentos abiertos. La clave es pública: Origin limita navegadores, no autentica servidores. Tras el consentimiento los adaptadores ejecutan integraciones reales: usa cuentas de prueba. No se crea otra web con licencia ni staging vinculado. Renovar borra solo datos de prueba y exige verificar staging de nuevo.
Límites: 10 orígenes, 20 configuraciones, 1000 decisiones y 1000 visitas. Renueva la clave para reiniciar.
Staging público debe ser un subdominio propio del dominio de producción, por ejemplo staging.example.com para example.com o www.example.com (solo se omite un prefijo www. inicial); añade otro sitio para probar en otro dominio.
<script>
window.addEventListener('biscotti:ready', ({ detail }) => {
console.assert(detail.environment === 'development');
});
</script>
<script src="https://biscotti.dev/consent.js" data-site-key="YOUR_TEST_KEY" defer></script>
<script type="text/plain" data-consent-category="analytics">
document.documentElement.dataset.testAnalytics = 'active';
</script>
<button type="button" data-biscotti-preferences>Cookies</button>Cambiar y retirar el consentimiento
Mantén este control en el pie de página. openPreferences abre los ajustes sin conceder consentimiento. Si se han ejecutado adaptadores del SDK, incluso una retirada parcial requiere recargar el documento para eliminar su código activo.
Escucha biscotti:beforereload en window. BeforeReloadDetail está congelado e incluye el SdkSnapshot actual, reason (choice, storage, expiry o error) y navigation (reload o replace). En una elección explícita, el guardado, la actualización de Consent Mode, el cierre de las puertas y consentchange preceden al evento. La denegación de emergencia primero deniega localmente, intenta actualizar Consent Mode y borrar las preferencias (la sincronización entre pestañas no borra la elección de otra pestaña), emite consentchange si cambia el estado y cualquier error, después beforereload y replace con denegación forzada. Si falla Consent Mode, las puertas siguen cerradas y error informa del fallo.
Guarda el estado de la aplicación de forma síncrona, por ejemplo un identificador de vista en sessionStorage. El evento no se puede cancelar; preventDefault, las excepciones del listener y los Promise devueltos no cancelan la navegación. El SDK no espera operaciones asíncronas. El código síncrono del listener se ejecuta antes de navegar. La señal cubre cambios locales, otra pestaña, caducidad y denegación de emergencia; no se emite si no hace falta recargar. Las cookies y el procesamiento del servidor necesitan sus propios controles. Una cookie sin firma no autentica al usuario.
Si eliges quedarte en el diálogo beforeunload del navegador, una retirada posterior vuelve a emitir la señal e intenta navegar de nuevo.
<button type="button" data-biscotti-preferences>Ajustes de cookies</button>window.addEventListener('biscotti:beforereload', ({ detail }) => {
try {
sessionStorage.setItem('app:view', document.documentElement.dataset.currentView ?? 'home');
} catch { /* Storage may be unavailable; consent withdrawal still proceeds. */ }
document.documentElement.dataset.biscottiReloadReason = detail.reason;
});CSP, CORS e integridad
Permite el origen de plataforma en script-src y connect-src, los recursos /brand/fonts en font-src y tus integraciones autorizadas. Los adaptadores inline necesitan nonce/hash CSP; se copia el nonce del script. El SDK inserta estilos Shadow DOM sin propagar nonce de estilo: una política estricta debe contemplarlo; el nonce del script no basta. No hay un CSP universal. CORS admite el host publicado; el SDK omite credentials y CORS no autentica servidores. Pega el código una vez. Recibirás las correcciones del script automáticamente, sin sustituir el código a mano. Las instalaciones existentes y WordPress funcionan igual. Actualizaciones automáticas (recomendado): /consent.js. Versión fijada con SRI (avanzado): /sdk/<SHA-256>/consent.js, integrity SHA-384, crossorigin="anonymous". El SDK público y los manifiestos permiten CORS *, pero la API sigue verificando el host del sitio. SRI verifica JavaScript, no la configuración API, las fuentes ni etiquetas externas. /consent.js sigue siendo una URL mutable para instalaciones existentes y WordPress; no fijes SRI a esa URL.
El banner puede usar configuración validada en localStorage durante 15 minutos, separada por plataforma, clave y origen. Cada documento confirma publicación y host con el servidor; ETag/304 evita retransmitir una configuración idéntica. La aceptación y los scripts opcionales siguen bloqueados hasta confirmar; puedes rechazar de inmediato. El rechazo durante un fallo elimina el permiso local; el registro de auditoría necesita configuración actual. Publicar o cambiar consentEpoch devuelve configuración nueva; retirar la publicación, eliminar el host o recibir errores borra la caché y deniega permisos. La caché no añade retraso de autorización en un documento nuevo; tras 8 segundos sin respuesta se mantiene el rechazo. Un documento abierto conserva su copia hasta navegar, sin consultas periódicas: no hay plazo de propagación para una pestaña siempre abierta. La primera visita sin caché espera al servidor. dev_ no usa esta caché. No necesitas actualizar el código de instalación.
Errores y verificación
Usa code estable, no Error.message. whenReady rechaza con BiscottiInitializationError al fallar el inicio; getLastError conserva el último error incluso después de una elección correcta. phase es initialization/runtime independientemente del estado final. Los fallos rechazan categorías opcionales. El error de descarga del SDK se maneja en script. Revisa Network/Console, host, clave, CORS, CSP, almacenamiento y publicación. Prueba visita inicial, rechazo, consentimiento parcial/completo, regreso, otra pestaña, retirada, API y storage inaccesibles. Verifica las etiquetas GTM y plugins reales por separado.
code | Comportamiento |
|---|---|
INVALID_SITE_KEY | data-site-key ausente o inválido. |
CONFIGURATION_UNAVAILABLE | Fallo de red, timeout, tamaño o respuesta de configuración fuera de 2xx. |
INVALID_CONFIGURATION | JSON/esquema, clave o host incorrectos. |
STORAGE_UNAVAILABLE | Storage/cookie inaccesibles o estructura dañada. |
CONSENT_MODE_FAILED | No se pudo actualizar dataLayer. |
ADAPTER_FAILED | Fallo o timeout del adaptador. |
SYNC_FAILED | Cola de auditoría dañada o bloqueada permanentemente; un fallo transitorio solo deja la decisión en cola. |
INITIALIZATION_FAILED | Otro fallo de interfaz o escuchas durante el inicio. |
Versiones e historial
API v1 del despliegue b0449f1, 2026-10-17. apiVersion identifica el contrato; publicationVersion la configuración; consentEpoch la renovación. No son URLs inmutables. Los cambios aditivos conservan v1; retirar métodos o cambiar su significado requiere otra versión.
2026-10-17 — Métodos de disponibilidad/estado/error, tres eventos DOM y snapshots congelados; métodos anteriores conservados. Corregidos recuperación SPA, fase explícita y nombre BiscottiInitializationError.
W16 — Modo de desarrollo. Clave independiente para pruebas en localhost y staging. Los datos de prueba no se incluyen en informes de producción ni facturación.
Actualizaciones automáticas (recomendado): Pega el código una vez. Recibirás las correcciones del script automáticamente, sin sustituir el código a mano. Las instalaciones existentes y WordPress funcionan igual. Versión fijada con SRI (avanzado): Eliges una versión concreta del script. No recibirás correcciones automáticas, incluidas las de seguridad. Para actualizarla debes sustituir el código a mano. En ambos modos, los ajustes publicados del banner se obtienen automáticamente.
W20 — Versiones SDK y SRI. /sdk/manifest.json enumera la versión actual y las conservadas. src es la URL inmutable, integrity su hash SHA-384 y bytes su tamaño. Cada versión tiene también un manifiesto junto al script. La URL contiene el hash SHA-256 y se almacena un año como immutable; cambiar la configuración no la modifica.
Para una versión fijada con SRI: al actualizar, guarda el código anterior y sustituye juntos src e integrity desde el panel o manifiesto. Mantén la clave y crossorigin="anonymous". Prueba disponibilidad, rechazo, consentimiento y retirada en localhost o staging. Para volver, restaura la pareja src/integrity anterior. El build no sobrescribe ni elimina versiones conservadas. Las versiones fijadas requieren actualización manual, también por seguridad; no evites un fallo SRI eliminando integrity. El hash no es apiVersion, publicationVersion ni consentEpoch. Volver al script anterior no revierte publicaciones ni datos; comprueba la compatibilidad con la API antes de volver.
W17 — biscotti:beforereload precede a reload/replace cuando se requiere tras retirar el consentimiento. BeforeReloadDetail = SdkSnapshot + reason + navigation. El código síncrono del listener en window se ejecuta antes de navegar. El evento no permite cancelar la navegación; el SDK no espera operaciones asíncronas. Consulta el orden y el ejemplo en la sección de retirada.
{
"apiVersion": 1,
"version": "9c2bc03275827be7431adcc709a295afb1f7778553dbaf3c3c4f8a2df233f702",
"src": "/sdk/9c2bc03275827be7431adcc709a295afb1f7778553dbaf3c3c4f8a2df233f702/consent.js",
"integrity": "sha384-VtJRmbHcw1tU2ZG0b1SkNpDBvF0Wk2fuFyZG/CNH7stj+z9FgY8pbYK6eyk5KqmD",
"bytes": 83837
}