Saltar al contenido principal
Incrusta el Editor de Plantillas de Firma dentro de tu aplicación usando autenticación JWT para un acceso seguro y de tiempo limitado. Esto es ideal para integraciones de marca blanca y aplicaciones multiinquilino.

Casos de uso

  • Edición de plantillas de marca blanca: Permite que los usuarios editen plantillas bajo tu marca
  • Aplicaciones multiinquilino: Acceso seguro a plantillas por usuario sin exponer claves API
  • Flujos de trabajo incrustados: Creación de plantillas sin fisuras dentro de tu producto
  • Acceso de tiempo limitado: Los tokens expiran automáticamente por seguridad

Cómo funciona

  1. Tu servidor solicita un token JWT a la API de Firma usando tu clave API
  2. Firma devuelve un token JWT de corta duración con el ID de la plantilla
  3. Tu frontend incrusta el editor con el token JWT
  4. El token expira automáticamente (expiración configurable)
Límite de tasa: Los endpoints JWT admiten 120 solicitudes por minuto por clave API para aplicaciones de alto volumen.

Autenticación JWT

Generar token JWT

Genera un token JWT para una plantilla específica usando el endpoint /generate-template-token. Endpoint: POST /generate-template-token Cuerpo de la solicitud:
{
  "companies_workspaces_templates_id": "123e4567-e89b-12d3-a456-426614174000"
}
Respuesta (201 Created):
{
  "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2024-04-20T10:00:00Z",
  "jwt_record_id": "jwt123-e89b-12d3-a456-426614174000"
}
Encabezados de límite de tasa:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1729180800

Guía de implementación

Seguridad: Nunca expongas tu clave API en código del lado del cliente. Genera siempre los tokens JWT desde tu backend seguro.

Backend: Generar token JWT

Llama a la API de Firma desde tu backend para generar un token JWT. El endpoint de tu backend debe aceptar un ID de plantilla y devolver el JWT a tu frontend. Ejemplo en Node.js:
// Example: Simple backend function to generate JWT
async function generateTemplateToken(templateId) {
  const response = await fetch('https://api.firma.dev/functions/v1/signing-request-api/generate-template-token', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FIRMA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      companies_workspaces_templates_id: templateId
    })
  })
  
  const data = await response.json()
  return data.token
}
Ejemplo en Python:
import os
import requests

def generate_template_token(template_id):
    response = requests.post(
        'https://api.firma.dev/functions/v1/signing-request-api/generate-template-token',
        headers={
            'Authorization': f'Bearer {os.getenv("FIRMA_API_KEY")}',
            'Content-Type': 'application/json'
        },
        json={
            'companies_workspaces_templates_id': template_id
        }
    )
    data = response.json()
    return data['token']

Implementación en frontend — HTML / JavaScript nativo

<!-- Load the Firma Template Editor library -->
<script src="https://api.firma.dev/functions/v1/embed-proxy/template-editor.js"></script>

<!-- Create a container for the editor -->
<div id="template-editor-container"></div>

<script>
  // Assuming you've already fetched the JWT from your backend
  const jwt = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...';
  const templateId = '08f72e3b-79a8-4eac-a268-b3f9efaf6573';
  
  // Initialize the template editor
  const editor = new FirmaTemplateEditor({
    container: '#template-editor-container',
    jwt: jwt,
    templateId: templateId,
    
    // Optional configuration
    width: '100%',        // Custom width (default: '100%')
    height: '100vh',      // Custom height (default: '100vh')
    showCloseButton: true, // Show close button in header
    
    // Optional callbacks
    onSave: (data) => {
      console.log('Template saved:', data);
    },
    onClose: (data) => {
      if (data.hasUnsavedChanges) {
        // Prompt user to save before leaving
      }
      console.log('Editor closed, unsaved changes:', data.hasUnsavedChanges);
    },
    onError: (error) => {
      console.error('Editor error:', error);
    },
    onLoad: (template) => {
      console.log('Editor loaded successfully:', template);
    }
  });
</script>

Implementación en frontend — React

import { useEffect, useRef, useState } from 'react';

function TemplateEditorComponent({ templateId, jwt }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const editorRef = useRef<any>(null);
  const [isLoaded, setIsLoaded] = useState(false);

  // Load the Firma Template Editor library
  useEffect(() => {
    const script = document.createElement('script');
    script.src = 'https://api.firma.dev/functions/v1/embed-proxy/template-editor.js';
    script.async = true;
    
    script.onload = () => setIsLoaded(true);
    document.body.appendChild(script);
    
    return () => {
      if (document.body.contains(script)) {
        document.body.removeChild(script);
      }
    };
  }, []);

  // Initialize the editor when library and JWT are ready
  useEffect(() => {
    if (!isLoaded || !containerRef.current || !jwt) return;

    editorRef.current = new window.FirmaTemplateEditor({
      container: containerRef.current,
      jwt: jwt,
      templateId: templateId,
      
      // Optional configuration
      theme: 'dark',        // 'light' | 'dark' (default: 'dark')
      readOnly: false,      // Disable editing (default: false)
      width: '100%',        // Custom width (default: '100%')
      height: '100vh',      // Custom height (default: '100vh')
      showCloseButton: true, // Show close button in header
      
      // Optional callbacks
      onSave: (data) => console.log('Saved:', data),
      onClose: (data) => {
        if (data.hasUnsavedChanges) {
          // Prompt user to save before leaving
        }
      },
      onError: (error) => console.error('Error:', error),
      onLoad: (template) => console.log('Loaded:', template)
    });

    return () => {
      if (editorRef.current?.destroy) {
        editorRef.current.destroy();
      }
    };
  }, [isLoaded, jwt, templateId]);

  return <div ref={containerRef} className="w-full h-full" />;
}

export default TemplateEditorComponent;

Opciones de configuración

OpciónTipoDescripción
containerHTMLElement | stringElemento del DOM o selector CSS donde montar el editor (obligatorio)
jwtstringToken de autenticación de tu backend (obligatorio)
templateIdstringIdentificador de la plantilla (obligatorio)
theme'dark' | 'light'Tema del editor (predeterminado: 'dark')
readOnlybooleanHabilita el modo de solo lectura (predeterminado: false)
heightstringAlto del contenedor (predeterminado: '100vh')
widthstringAncho del contenedor (predeterminado: '100%')
showCloseButtonbooleanMuestra un botón de cierre en el encabezado del editor (predeterminado: false)
onSavefunctionCallback cuando se guarda la plantilla
onClosefunctionCallback cuando se hace clic en el botón de cierre o se llama a triggerClose(). Recibe { hasUnsavedChanges: boolean }
onErrorfunctionCallback para el manejo de errores
onLoadfunctionCallback cuando el editor carga con los datos de la plantilla

Métodos de instancia

MétodoDescripción
triggerClose()Activa el flujo de cierre de forma programática. Comprueba si hay cambios sin guardar e invoca onClose con { hasUnsavedChanges: boolean }
Usa triggerClose() cuando tu aplicación anfitriona necesite cerrar el editor desde su propia interfaz, por ejemplo, ante un evento de navegación del padre o un botón “atrás”:
// Close the editor from your app's UI
document.getElementById('my-back-button').addEventListener('click', () => {
  editor.triggerClose();
});

Eventos postMessage (editor → anfitrión)

El editor de Firma emitirá eventos postMessage para acciones importantes del ciclo de vida. A continuación encontrarás un esquema de eventos recomendado y mínimo que puedes implementar para reaccionar a los guardados y publicaciones del editor. Si tienes un esquema canónico en tu plataforma, reemplaza estos por los nombres de evento oficiales de tu sistema.
Usa estos eventos para hacer seguimiento de la actividad del editor sin realizar llamadas API adicionales, lo que te ayuda a mantenerte dentro de los límites de tasa.
Envoltorio del evento (payload de window.postMessage):
{
	"type": "editor.event",
	"event": "editor.saved", // or editor.published, editor.closed
	"payload": {
		"template_id": "tmpl_123",
		"updated_at": "2025-09-04T12:34:56Z",
		"draft": false
	}
}

Ejemplo de listener en el cliente (JS puro)

window.addEventListener('message', (ev) => {
	// 1) Validate origin
	if (ev.origin !== 'https://app.firma.dev') return
	// 2) Validate shape
	const data = ev.data || {}
	if (data.type !== 'editor.event') return

	switch (data.event) {
		case 'editor.saved':
			console.log('Template saved', data.payload)
			// Optionally fetch the latest template via API
			break
		case 'editor.published':
			console.log('Template published', data.payload)
			break
		case 'editor.closed':
			console.log('Editor closed')
			break
		default:
			break
	}
})

Gestión del ciclo de vida del token

Los tokens JWT se generan con tiempo de expiración suficiente para sesiones de edición típicas. El editor gestionará automáticamente la expiración del token.

Expiración automática

Los tokens JWT expiran automáticamente según la marca de tiempo expires_at. Tras la expiración:
  • El editor incrustado rechazará el token
  • Los usuarios deben solicitar un nuevo token para continuar
  • No se necesita ninguna llamada API: los tokens expiran de forma pasiva

Límite de tasa

Consulta la guía sobre Límites de Tasa.

Prácticas recomendadas de seguridad

Nunca expongas tu clave API en código del lado del cliente. Genera siempre los tokens JWT desde un endpoint de servidor seguro.

✅ Qué hacer

  • ✅ Genera los tokens desde tu servidor backend
  • ✅ Supervisa los límites de tasa
  • ✅ Usa HTTPS para todas las solicitudes API

❌ Qué no hacer

  • ❌ No expongas claves API en código de frontend
  • ❌ No reutilices tokens entre usuarios
  • ❌ No registres tokens JWT en logs (riesgo de seguridad)
  • ❌ No compartas tokens entre distintas plantillas

Solución de problemas

Error de token expirado

Síntoma: El editor muestra “Token expired” o un error de autenticación Solución:
  • Implementa la renovación del token antes de que expire
  • Genera un nuevo token y recarga el iframe
  • Comprueba la sincronización del reloj del sistema

401 Unauthorized

Síntoma: La generación del JWT falla con 401 Posibles causas:
  • Clave API inválida o ausente
  • La clave API no tiene los permisos necesarios
  • La clave API está deshabilitada
Solución: Verifica la clave API en el panel y comprueba los permisos

404 Not Found

Síntoma: Plantilla no encontrada al generar el JWT Posibles causas:
  • El ID de la plantilla no existe
  • La plantilla pertenece a otro espacio de trabajo
  • La plantilla fue eliminada
Solución: Verifica el ID de la plantilla y el acceso al espacio de trabajo

Límite de tasa excedido

Síntoma: 429 Too Many Requests Solución:
  • Implementa el almacenamiento en caché de tokens
  • Aumenta el tiempo de expiración del token
  • Espera a que se reinicie el límite de tasa (comprueba el encabezado X-RateLimit-Reset)
  • Implementa lógica de reintento con backoff

Próximos pasos