Introducción
Esta documentación cubre el proceso completo de firma digital de contratos:
- Envío inicial: Enviar contratos para firma digital por primera vez
- Reenvío: Reenviar solicitudes cuando el cliente no recibió la comunicación o necesita un recordatorio
- Consulta de estado: Obtener información en tiempo real del proceso de firma
- Auditoría: Datos detallados de cada firmante (IP, fecha/hora, estado)
✅ Características Principales
- Operaciones síncronas: Respuestas inmediatas (< 2 segundos)
- Múltiples canales: Email, SMS, o Email con OTP
- Reenvío ilimitado: Puedes reenviar las veces que necesites
- Trazabilidad completa: Cada operación genera un request_id único
- Validación de permisos: Solo puedes gestionar firmas de tus contratos
- Notificaciones automáticas: Webhooks cuando se completa la firma
Envío Inicial de Firma
El endpoint POST /firma permite enviar contratos ya creados en el sistema para firma digital del cliente. El contrato debe haber sido creado previamente mediante los endpoints de contratación.
ℹ️ ¿Cuándo usar este endpoint?
Utiliza este endpoint cuando:
- Creaste un contrato con
no_enviar_firma: true - Quieres controlar el momento exacto del envío de firma
- Necesitas elegir el canal de envío específico (email, SMS, email_otp)
Parámetros del Request
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
contrato_id |
integer | ✅ Sí | ID del contrato. Se obtiene del campo contrato_result.id en la respuesta del endpoint de contratación |
canal_envio |
string | ✅ Sí | Canal de envío: "email", "sms", o "email_otp" |
direcciones_firma |
string | ✅ Sí | Email, teléfono, o ambos separados por ; |
referencia_externa |
string | ❌ No | Referencia opcional para tracking interno (ej: "DEAL-12345") |
Canales de Envío
📧 Email ("email")
Envío del enlace de firma por correo electrónico. Canal más común y recomendado.
- Formato:
direcciones_firma: "usuario@example.com" - Validación: Email en formato RFC estándar
- Ventaja: El cliente puede firmar desde cualquier dispositivo
📱 SMS ("sms")
Envío del enlace de firma por mensaje de texto SMS.
- Formato:
direcciones_firma: "606123456"o"+34606123456" - Validación: 8-15 dígitos, opcional prefijo internacional +
- Ventaja: Entrega inmediata, ideal para clientes sin email
🔐 Email con OTP ("email_otp")
Envío dual: email con enlace + SMS con código OTP de verificación.
- Formato:
direcciones_firma: "usuario@example.com;606123456" - Ventaja: Máxima seguridad con doble factor de autenticación
Ejemplo de Request
POST /firma
Authorization: Bearer <tu-jwt-token>
Content-Type: application/json
{
"contrato_id": 369877,
"canal_envio": "email",
"direcciones_firma": "cliente@example.com",
"referencia_externa": "ZOHO-DEAL-12345"
}
Respuesta Exitosa (200)
{
"request_id": 12345,
"firma_result": {
"status": "success",
"message": "Contrato enviado para firma digital",
"circuito_id": "123456"
},
"referencia_externa": "ZOHO-DEAL-12345"
}
💡 Importante: Guarda el circuito_id
La respuesta incluye un circuito_id que necesitarás para:
- Reenviar la solicitud de firma (endpoint
POST /firma/reenviar) - Consultar el estado del proceso (endpoint
GET /firma/{circuito_id})
Flujo de Uso Típico
- Paso 1: Crear el contrato con
no_enviar_firma: true - Paso 2: Obtener el
contrato_idde la respuesta - Paso 3: Enviar a firma cuando estés listo:
POST /firma { "contrato_id": 369877, "canal_envio": "email", "direcciones_firma": "cliente@example.com" } - Paso 4: Guardar el
circuito_idde la respuesta para operaciones posteriores
Reenviar Solicitud de Firma
Reenvía una solicitud de firma existente al cliente. Útil para recordatorios o cuando el cliente no recibió la comunicación inicial.
Parámetros del Request
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
circuito_id |
String | ✅ Sí | ID de la solicitud de firma en el sistema. Se obtiene en la respuesta de POST /firma |
mode |
String | ❌ No | Modo de firma: ds (drawing signature), os (one-time certificate), ud (upload & draw). Por defecto: ds |
referencia_externa |
String | ❌ No | Tu referencia interna para tracking. Máximo 100 caracteres |
Ejemplo de Request
POST /firma/reenviar
Authorization: Bearer <tu-jwt-token>
Content-Type: application/json
{
"circuito_id": "123456",
"mode": "ds",
"referencia_externa": "REENVIO-2024-001"
}
Respuesta Exitosa (200)
{
"request_id": 789,
"circuito_id": "123456",
"estado": "reenviada",
"mensaje": "Solicitud de firma reenviada exitosamente",
"referencia_externa": "REENVIO-2024-001"
}
⚠️ Validación de Permisos
El sistema verifica que tu canal JWT tenga autorización sobre el contrato asociado al circuito_id. Si no tienes permisos, recibirás un error 403.
Consultar Estado de Firma
Obtiene información detallada del estado actual de una solicitud de firma, incluyendo datos de todos los firmantes y su progreso individual.
Parámetros del Request
| Parámetro | Ubicación | Tipo | Descripción |
|---|---|---|---|
circuito_id |
Path | String | ID de la solicitud de firma |
referencia_externa |
Query (opcional) | String | Tu referencia interna para tracking |
Ejemplo de Request
GET /firma/123456?referencia_externa=CONSULTA-001 Authorization: Bearer <tu-jwt-token>
Respuesta Exitosa (200)
{
"request_id": 790,
"circuito_id": "123456",
"estado": "en_proceso",
"en_proceso": true,
"fecha_inicio": "2024-06-09T10:30:00+02:00",
"fecha_completado": null,
"firmantes": [
{
"email": "juan.perez@example.com",
"estado": "firmado",
"fecha_firma": "2024-06-09T15:45:32+02:00"
},
{
"email": "maria.lopez@example.com",
"estado": "pendiente",
"fecha_firma": null
}
],
"referencia_externa": "CONSULTA-001"
}
📊 Estados Posibles
- pendiente: Solicitud creada pero aún no iniciada
- en_proceso: Al menos un firmante ha iniciado el proceso
- completado: Todos los firmantes han firmado
- rechazado: La solicitud fue rechazada
// Ejemplo de respuesta cuando está completado
{
"request_id": 791,
"circuito_id": "123456",
"estado": "completado",
"en_proceso": false,
"fecha_inicio": "2024-06-09T10:30:00+02:00",
"fecha_completado": "2024-06-09T18:06:40+02:00",
"firmantes": [
{
"email": "juan.perez@example.com",
"estado": "firmado",
"fecha_firma": "2024-06-09T15:45:32+02:00"
},
{
"email": "maria.lopez@example.com",
"estado": "firmado",
"fecha_firma": "2024-06-09T18:06:40+02:00"
}
],
"referencia_externa": "CONSULTA-001"
}
Campos de Firmantes
| Campo | Descripción |
|---|---|
email |
Dirección de email del firmante |
estado |
firmado: El firmante completó su firma pendiente: Aún no ha firmado |
fecha_firma |
Timestamp ISO 8601 del momento exacto de la firma (null si estado = pendiente) |
✅ Notificación Automática
Cuando todos los firmantes completan su firma, el sistema:
- Cambia el contrato a Estado: 1, Subestado: 50 (Firmado)
- Envía un webhook automático si tienes configuradas notificaciones de cambios de estado
- Registra el evento completo en
webhooks.event_evidenceconevent='process_completed'
Autenticación
Todos los endpoints de gestión de firma requieren autenticación JWT. Para obtener información detallada sobre cómo autenticarse, consulta la documentación de autenticación.
Gestión de Errores
Códigos de Error Comunes
| Código | Descripción | Solución |
|---|---|---|
| 400 | Parámetro requerido faltante o inválido | Verifica que circuito_id esté presente y sea válido |
| 401 | Token JWT ausente o inválido | Incluye un token JWT válido en el header Authorization |
| 403 | Sin permisos sobre el circuito_id | Este circuito_id pertenece a otro canal. Solo puedes gestionar firmas de tus propios contratos |
| 404 | Circuito_id no encontrado | El circuito_id no existe en el sistema o nunca fue enviado a firma |
| 500 | Error interno del servidor | Contacta con soporte técnico |
Ejemplo de Respuesta de Error
{
"error": "Circuito no encontrado o sin permisos",
"request_id": 791,
"details": {
"circuito_id": "999999",
"canal": "mi-canal"
},
"referencia_externa": "REF-123"
}
Ejemplos Completos de Integración
Ejemplo 1: Reenviar Firma (Python)
import requests
# Tu token JWT
jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
headers = {
"Authorization": f"Bearer {jwt_token}",
"Content-Type": "application/json"
}
# Reenviar solicitud de firma
payload = {
"circuito_id": "123456",
"mode": "ds",
"referencia_externa": "REENVIO-2024-001"
}
response = requests.post(
"https://pre-webhooks.imaginaenergia.com/firma/reenviar",
headers=headers,
json=payload
)
if response.status_code == 200:
result = response.json()
print(f"✅ Recordatorio enviado. Request ID: {result['request_id']}")
else:
print(f"❌ Error: {response.json()['error']}")
Ejemplo 2: Consultar Estado (Python)
import requests
# Tu token JWT
jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
headers = {"Authorization": f"Bearer {jwt_token}"}
# Consultar estado de firma
circuito_id = "123456"
response = requests.get(
f"https://pre-webhooks.imaginaenergia.com/firma/{circuito_id}",
headers=headers
)
if response.status_code == 200:
data = response.json()
progreso = data['progreso']
print(f"📊 Estado de firma del circuito {circuito_id}")
print(f"Total firmantes: {progreso['total_firmantes']}")
print(f"Completados: {progreso['firmantes_completados']}")
print(f"Progreso: {progreso['porcentaje_completado']}%")
print("\n👥 Firmantes:")
for firmante in data['firmantes']:
estado = "✅ Firmado" if firmante['completado'] else "⏳ Pendiente"
print(f" {firmante['nombre']}: {estado}")
if firmante['completado']:
print(f" 📅 Fecha: {firmante['fecha_firma']}")
print(f" 🌐 IP: {firmante['ip_firma']}")
else:
print(f"❌ Error: {response.json()['error']}")
Ejemplo 3: Workflow Completo (JavaScript)
// Función para reenviar firma y verificar estado
async function gestionarFirmaContrato(circuitoId, jwtToken) {
const baseUrl = "https://pre-webhooks.imaginaenergia.com";
const headers = {
"Authorization": `Bearer ${jwtToken}`,
"Content-Type": "application/json"
};
try {
// 1. Reenviar solicitud de firma
console.log("🔄 Reenviando solicitud de firma...");
const reenvioResponse = await fetch(`${baseUrl}/firma/reenviar`, {
method: "POST",
headers: headers,
body: JSON.stringify({
circuito_id: circuitoId,
mode: "ds",
referencia_externa: `REENVIO-${Date.now()}`
})
});
if (!reenvioResponse.ok) {
throw new Error(`Error en reenvío: ${reenvioResponse.statusText}`);
}
const reenvioData = await reenvioResponse.json();
console.log(`✅ Recordatorio enviado. Request ID: ${reenvioData.request_id}`);
// 2. Esperar un momento y consultar estado
await new Promise(resolve => setTimeout(resolve, 2000));
console.log("\n🔍 Consultando estado actual...");
const estadoResponse = await fetch(`${baseUrl}/firma/${circuitoId}`, {
method: "GET",
headers: headers
});
if (!estadoResponse.ok) {
throw new Error(`Error consultando estado: ${estadoResponse.statusText}`);
}
const estadoData = await estadoResponse.json();
const progreso = estadoData.progreso;
console.log(`\n📊 Progreso: ${progreso.porcentaje_completado}%`);
console.log(` ${progreso.firmantes_completados}/${progreso.total_firmantes} firmantes han completado`);
// 3. Mostrar estado de cada firmante
console.log("\n👥 Estado de firmantes:");
estadoData.firmantes.forEach(firmante => {
const estado = firmante.completado ? "✅ Firmado" : "⏳ Pendiente";
console.log(` ${firmante.nombre} (${firmante.email}): ${estado}`);
if (firmante.completado) {
console.log(` 📅 ${firmante.fecha_firma}`);
}
});
return {
exito: true,
progreso: progreso,
firmantes: estadoData.firmantes
};
} catch (error) {
console.error("❌ Error:", error.message);
return {
exito: false,
error: error.message
};
}
}
// Uso
gestionarFirmaContrato("123456", "tu-jwt-token-aqui");
💡 Mejores Prácticas
- No reenvíes demasiado frecuentemente: Espera al menos 1 hora entre reenvíos para no saturar al cliente
- Consulta el estado antes de reenviar: Verifica si el cliente ya firmó antes de enviar recordatorios
- Usa referencia_externa: Facilita el tracking y debugging de operaciones
- Registra las IPs de firma: Útil para auditorías y compliance
- Configura webhooks: En lugar de polling constante, recibe notificaciones automáticas cuando se completa la firma