Errores del flujo embebido

Taxonomía completa de los códigos de error que puede recibir tu integración embebida: qué significa cada uno, quién lo emite, si es terminal o reintentable, y qué hacer.

Cómo te llegan los errores

Hay dos canales, según la capa donde ocurre el error:

  1. Excepción síncrona de AllSign.init() — errores de validación local del SDK (secret faltante, formato inválido, secret key en el browser). Se lanzan como AllSignError con un code estable.

  2. Callback onExit con metadata.error.code — errores en tiempo de ejecución dentro del iframe (init contra el backend, firma, verificación de identidad). El iframe emite allsign:error y el SDK te lo entrega verbatim en onExit — el SDK no normaliza códigos, así que puedes recibir códigos que tu versión del SDK aún no cataloga.

import { AllSign, AllSignError, isRetryableError } from '@allsign/embedded';

try {
  const session = AllSign.init({
    clientSecret,
    onExit: (metadata) => {
      const code = metadata.error?.code;
      if (!code) return; // salida limpia (completada o cancelada por el firmante)
      console.error('Error embebido:', code, metadata.error?.message);
    },
  });
  session.modal();
} catch (e) {
  if (e instanceof AllSignError) {
    // Error de validación local — corrige tu integración
    console.error(e.code, e.message);
  }
}

Terminal vs reintentable

Cada código cae en una de dos clases, y la clase determina la recuperación:

  • Reintentable (transitorio) — la falla es de infraestructura, no de la sesión. El mismo client_secret sigue siendo válido y re-intercambiable durante toda su ventana de 15 minutos: re-invoca AllSign.init con él (idealmente con backoff). Son reintentables: NETWORK_ERROR, BACKEND_UNREACHABLE, INVALID_BACKEND_RESPONSE, INTERNAL_ERROR, INIT_TIMEOUT y cualquier HTTP_5xx.

  • Terminal — la sesión quedó inutilizable (expiró, se completó, se canceló, es inválida o está mal configurada). Reintentar con el mismo client_secret no puede funcionar. Recuperación: tu servidor crea una sesión nueva y tu frontend re-invoca init (ver Recuperación). Todo código que no esté en la lista reintentable — incluidos códigos desconocidos — es terminal.

Desde la versión 0.3.0 del SDK, los helpers isRetryableError(code) / isTerminalError(code) implementan exactamente esta clasificación (incluyendo el patrón HTTP_5xx):

import { isRetryableError, isTerminalError } from '@allsign/embedded';

isRetryableError('BACKEND_UNREACHABLE'); // true
isRetryableError('HTTP_503');            // true
isTerminalError('SESSION_EXPIRED');      // true
isTerminalError('CODIGO_FUTURO');        // true — default seguro

Catálogo completo

CódigoHTTPDónde se originaTipoAcción recomendada
MISSING_CLIENT_SECRET— / 400SDK (AllSign.init) y proxy de initTerminalObtén el client_secret de tu backend (POST /v2/signing-sessions) antes de llamar init.
INVALID_CLIENT_SECRET_FORMATSDK (AllSign.init)TerminalPasa el client_secret tal cual lo devuelve la API — sin truncar ni re-codificar.
SECRET_KEY_PASSED_AS_CLIENT_SECRETSDK (AllSign.init)TerminalBug de seguridad en tu integración: la secret key nunca debe llegar al navegador. Crea la sesión server-side y manda solo el client_secret.
INVALID_REQUEST400Proxy de init (dashboard)TerminalEl body no era JSON válido. Solo ocurre llamando el endpoint a mano — usa el SDK.
BACKEND_UNREACHABLE502Proxy de init (dashboard)ReintentableReintenta init con el mismo client_secret (con backoff). Si persiste, reporta a soporte.
INVALID_BACKEND_RESPONSEpassthrough (usualmente 5xx)Proxy de init (dashboard)ReintentableEl backend respondió algo no-JSON. Reintenta con el mismo client_secret.
SESSION_INVALID400 / 500Backend (init)TerminalEl secret no corresponde a ninguna sesión (o la sesión está corrupta). Crea una sesión nueva; verifica que no mezclas environments (test vs live).
SESSION_EXPIRED410Backend (init) / iframe en flujoTerminalEl client_secret venció (TTL 15 min). Crea la sesión justo antes de montar el widget, no al cargar la página.
SESSION_COMPLETED409Backend (init)TerminalEl documento ya se firmó. Trátalo como éxito en tu UX; para otra firma, crea una sesión nueva.
SESSION_CANCELLED409Backend (init)TerminalLa sesión fue anulada server-side. Crea una sesión nueva si la firma debe proceder.
ORIGIN_NOT_ALLOWED403Backend (init)TerminalAgrega el dominio de TU app a allowed_origins al crear la sesión. http://localhost siempre está permitido en desarrollo.
RATE_LIMITED429Backend (init)TerminalDemasiados intentos de init desde la misma IP (10/min). Espera ~1 minuto antes de reintentar — no hagas loop.
INTERNAL_ERROR500Backend (init)ReintentableError inesperado del lado de AllSign. Reintenta con el mismo client_secret; si persiste, crea sesión nueva y reporta a soporte.
SESSION_NOT_FOUND404Backend (GET /v2/signing-sessions/{id}/policy)TerminalEl session_id no existe (o está mal formado). Verifícalo; crea una sesión nueva si ya no existe.
ALLOWED_ORIGINS_REQUIRED400Backend (POST /v2/signing-sessions) — lo ve TU servidorTerminalLas sesiones live requieren allowed_origins. Incluye el dominio donde montas el iframe (el de tu app, no el de AllSign).
SESSION_NOT_COMPLETED409Backend (POST /v2/signing-sessions/{id}/verify) — lo ve TU servidorTerminalLlamaste verify antes de que el firmante terminara. Hazlo después de onSuccess o del webhook document.completed.
NOT_EMBEDDEDPágina del iframeTerminalAlguien cargó /embed/sign/… directo en una ventana. No enlaces esa URL — monta el flujo con el SDK.
NETWORK_ERRORIframe / SDK (fetch del navegador)ReintentableReintenta init con el mismo client_secret. Si persiste, revisa la conectividad del firmante y tu CSP (connect-src).
INVALID_RESPONSEIframe (respuesta no-JSON)TerminalReintenta una vez con un init nuevo; si se reproduce, reporta a soporte.
INIT_TIMEOUTIframe (handshake / init)ReintentableRe-invoca init con el mismo secret. Si persiste, verifica que nada en tu página bloquee postMessage hacia/desde el iframe.
NO_FIELDSIframe (carga) / acción de firmaTerminalEl documento está en modo autógrafa pero no tiene campos de firma. Corrígelo server-side y crea una sesión nueva.
HTTP_{status}el statusIframe (fallback sin código legible)5xx reintentable / 4xx terminalHTTP_502 → reintenta; HTTP_404 → trata como terminal. isRetryableError ya distingue el patrón.
WRONG_SIGNING_MODEAcción de firma (iframe)TerminalEl documento usa Firma Simple y se enviaron campos de firma. Crea la sesión sobre un documento configurado correctamente.
SUBMIT_FAILEDAcción de firma (iframe)Terminal*El iframe reintenta inline con el firmante, así que rara vez te llega. Si te llega: sesión nueva; si se repite, reporta con el sessionId.
SIGNATURE_NOT_FOUNDAcción de pipeline (iframe)TerminalLa firma referenciada ya no existe (p. ej. documento borrado). Verifica el documento y crea una sesión nueva.
PIPELINE_FETCH_FAILEDAcción de pipeline (iframe)TerminalNo se pudieron cargar los pasos de verificación. Sesión nueva; reporta si es reproducible.
MODULE_UNSUPPORTEDPipeline de verificación (iframe)TerminalLa sesión exige un módulo de verificación que el flujo embebido no puede renderizar (y no se puede saltar). Ajusta los requisitos del documento o usa el link de firma no-embebido.
VERIFICATION_FAILEDPipeline de verificación (iframe)TerminalUn paso de verificación de identidad falló. Es un resultado del firmante, no un bug — decide tu flujo (nuevo intento con sesión nueva, o revisión manual).
VERIFICATION_EXPIREDPipeline de verificación (iframe)TerminalUn paso de verificación expiró antes de completarse. Crea una sesión nueva para que el firmante verifique de nuevo.
CSP_BLOCKEDSDKTerminalTu Content-Security-Policy bloqueó el iframe. Agrega https://allsign.io a frame-src.
CAMERA_DENIEDSDK / iframeTerminalEl firmante negó el permiso de cámara (necesario para IDV). Pídele habilitarlo y arranca un intento nuevo.
NOT_IMPLEMENTEDSDKTerminalLa feature aún no está en tu versión del SDK. Actualiza @allsign/embedded.

Recuperación

El patrón de recuperación es el mismo que usa Plaid Link: ante un error terminal, no "revivas" la sesión — crea una nueva. Las Signing Sessions son baratas de crear y el client_secret dura 15 minutos.

  1. Error reintentable → re-invoca AllSign.init con el mismo client_secret (sigue siendo re-intercambiable dentro de su ventana de 15 min; el backend rota el token interno y devuelve uno fresco). Usa un backoff corto.
  2. Error terminal → tu servidor crea una sesión nueva con POST /v2/signing-sessions (mismo document_id, mismo firmante) y tu frontend re-invoca init con el client_secret fresco.
import { AllSign, isRetryableError } from '@allsign/embedded';

function montarFirma(clientSecret) {
  const session = AllSign.init({
    clientSecret,
    onSuccess: (result) => actualizarUI(result),
    onExit: async (metadata) => {
      const code = metadata.error?.code;
      if (!code) return; // salida limpia — no hay nada que recuperar

      if (isRetryableError(code)) {
        // Transitorio: el MISMO client_secret sigue siendo válido
        setTimeout(() => montarFirma(clientSecret), 2000);
        return;
      }

      // Terminal: pide a TU backend una sesión NUEVA y re-monta
      const res = await fetch('/mi-api/signing-sessions', {
        method: 'POST',
        body: JSON.stringify({ documentId: miDocumentId }),
      });
      const { client_secret: fresco } = await res.json();
      montarFirma(fresco);
    },
  });
  session.modal();
}

Casos que NO debes reintentar en automático:

  • SESSION_COMPLETED — ya está firmado; muéstralo como éxito.
  • RATE_LIMITED — espera ~1 minuto; reintentar en loop solo alarga el bloqueo.
  • SECRET_KEY_PASSED_AS_CLIENT_SECRET, ORIGIN_NOT_ALLOWED, ALLOWED_ORIGINS_REQUIRED, CSP_BLOCKED, NOT_EMBEDDED — son errores de configuración de tu integración; ningún reintento los arregla.
  • VERIFICATION_FAILED — es un resultado de identidad del firmante; decide tu flujo de producto (reintento manual, revisión).

Reporte a soporte

Si un error persiste después de aplicar el patrón de recuperación, repórtalo con estos datos — aceleran el diagnóstico de días a minutos:

DatoDónde lo obtienes
code y messagemetadata.error en onExit (o la excepción de init)
sessionIdmetadata.sessionId en onExit, o el session_id de POST /v2/signing-sessions
Timestamp (con zona horaria)El momento del fallo — nos permite correlacionar logs del backend
Origen donde montas el iframewindow.location.origin de tu app
Versión del SDKnpm ls @allsign/embedded
¿Reproducible?Si pasa siempre, a veces, o pasó una vez

Canales: GitHub Issues del SDK para bugs del SDK, o tu canal de soporte de AllSign para errores de sesión/backend (incluye el sessionId — sin él no podemos rastrear la sesión).


Siguiente paso

Was this page helpful?