API de Cuestionarios
El módulo de Cuestionarios requiere plan Professional o superior. Las solicitudes desde un plan Starter recibirán HTTP 403 con código FEATURE_DISABLED. Consulta la guía funcional del módulo para más contexto.
El motor de cuestionarios permite crear plantillas, asignarlas a proveedores o usuarios internos, recolectar respuestas a través de un enlace público y calcular un puntaje de cumplimiento automáticamente.
Base URL: https://api.dpolab.com/api/v1/questionnaires
Prefijo público (sin auth): https://api.dpolab.com/api/v1/public/questionnaire
Resumen de endpoints
| Método | Endpoint | Rol mínimo | Descripción |
|---|---|---|---|
GET | /questionnaires/stats | VIEWER | Estadísticas globales de asignaciones |
GET | /questionnaires/templates | VIEWER | Listar plantillas |
POST | /questionnaires/templates | ADMIN | Crear plantilla |
GET | /questionnaires/templates/:id | VIEWER | Obtener plantilla con preguntas |
PUT | /questionnaires/templates/:id | ADMIN | Actualizar plantilla |
DELETE | /questionnaires/templates/:id | ADMIN | Eliminar plantilla (soft delete) |
POST | /questionnaires/templates/:id/questions | ADMIN | Agregar pregunta a plantilla |
PUT | /questionnaires/templates/:id/questions/:qId | ADMIN | Actualizar pregunta |
DELETE | /questionnaires/templates/:id/questions/:qId | ADMIN | Eliminar pregunta |
GET | /questionnaires/assignments | VIEWER | Listar asignaciones |
POST | /questionnaires/assignments | ANALYST+ | Crear asignación |
GET | /questionnaires/assignments/:id | VIEWER | Obtener asignación con respuestas |
PUT | /questionnaires/assignments/:id/expire | ADMIN | Expirar asignación |
POST | /questionnaires/assignments/:id/validate | ADMIN | Validar asignación completada |
GET | /public/questionnaire/:token | Público | Ver cuestionario (respondente) |
POST | /public/questionnaire/:token/respond | Público | Enviar respuestas |
Plantillas
Listar plantillas
GET /questionnaires/templatesQuery params opcionales:
| Param | Valores | Descripción |
|---|---|---|
templateType | PROVIDER_EVALUATION, INTERNAL_AUDIT, DPIA_SCREENING, BREACH_ASSESSMENT, CUSTOM | Filtrar por tipo |
includeDeleted | true | false | Incluir plantillas eliminadas (default: false) |
curl "https://api.dpolab.com/api/v1/questionnaires/templates?templateType=PROVIDER_EVALUATION" \
-H "Authorization: Bearer {token}"Respuesta (200):
{
"success": true,
"data": {
"templates": [
{
"id": "tpl_abc123",
"title": "Evaluación de Proveedor — Nivel 1",
"description": "Cuestionario base de seguridad para nuevos proveedores",
"templateType": "PROVIDER_EVALUATION",
"isDefault": false,
"isActive": true,
"isDeleted": false,
"questionCount": 12,
"createdBy": "usr_xyz",
"createdAt": "2026-03-01T10:00:00Z",
"updatedAt": "2026-03-15T08:30:00Z"
}
]
}
}Crear plantilla
POST /questionnaires/templatesRequiere rol ADMIN. Sujeto al límite del plan: Professional 10 plantillas, Business 50, Enterprise ilimitadas.
Body:
{
"title": "Evaluación de Proveedor — Nivel 2",
"description": "Cuestionario extendido para proveedores críticos",
"templateType": "PROVIDER_EVALUATION"
}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
title | string (2-300 chars) | Sí | Nombre de la plantilla |
description | string (máx. 2000 chars) | No | Descripción de propósito |
templateType | enum | Sí | Tipo: PROVIDER_EVALUATION, INTERNAL_AUDIT, DPIA_SCREENING, BREACH_ASSESSMENT, CUSTOM |
Respuesta (201):
{
"success": true,
"data": {
"template": {
"id": "tpl_new456",
"title": "Evaluación de Proveedor — Nivel 2",
"templateType": "PROVIDER_EVALUATION",
"isDefault": false,
"isActive": true,
"createdAt": "2026-05-10T09:00:00Z"
}
}
}Obtener plantilla
GET /questionnaires/templates/:idRetorna la plantilla completa incluyendo todas sus preguntas ordenadas por orderIndex.
curl https://api.dpolab.com/api/v1/questionnaires/templates/tpl_abc123 \
-H "Authorization: Bearer {token}"Respuesta (200):
{
"success": true,
"data": {
"template": {
"id": "tpl_abc123",
"title": "Evaluación de Proveedor — Nivel 1",
"templateType": "PROVIDER_EVALUATION",
"isDefault": false,
"isActive": true,
"questions": [
{
"id": "q_001",
"templateId": "tpl_abc123",
"questionText": "¿Cuenta con política de seguridad de la información vigente?",
"questionType": "YES_NO",
"options": null,
"isRequired": true,
"weight": 3,
"orderIndex": 0,
"section": "Seguridad",
"complianceRef": "ISO 27001 §5.2"
}
]
}
}
}Actualizar plantilla
PUT /questionnaires/templates/:idRequiere rol ADMIN. Las plantillas isDefault: true no se pueden editar.
Body (todos los campos opcionales):
{
"title": "Evaluación de Proveedor — Nivel 1 (revisado)",
"description": "Versión actualizada mayo 2026",
"isActive": true
}Eliminar plantilla
DELETE /questionnaires/templates/:idRequiere rol ADMIN. Las plantillas por defecto (isDefault: true) no se pueden eliminar. La eliminación es un soft delete: isDeleted = true, isActive = false.
Respuesta (200):
{
"success": true,
"data": { "id": "tpl_abc123", "softDeleted": true }
}Preguntas
Las preguntas se gestionan de forma anidada bajo su plantilla. Solo se pueden editar plantillas que no sean por defecto.
Tipos de pregunta y puntuación
| Tipo | Descripción | Puntuación automática |
|---|---|---|
YES_NO | Sí/No | yes = weight, resto = 0 |
SCALE_1_5 | Escala del 1 al 5 | (valor / 5) × weight |
MULTIPLE_CHOICE | Opción única o múltiple con pesos | Mejor peso seleccionado × weight |
DATE | Fecha | Presencia = weight, ausencia = 0 |
TEXT | Texto libre | Sin puntuación automática; queda en revisión manual |
Las preguntas de tipo TEXT no cuentan hacia el puntaje ni el puntaje máximo. Solo incrementan el contador pendingManualReview de la asignación, para que el validador sepa cuántas respuestas abiertas debe revisar.
Agregar pregunta
POST /questionnaires/templates/:id/questionsRequiere rol ADMIN.
curl -X POST https://api.dpolab.com/api/v1/questionnaires/templates/tpl_abc123/questions \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"questionText": "¿Realiza copias de seguridad con frecuencia mínima semanal?",
"questionType": "YES_NO",
"isRequired": true,
"weight": 2,
"section": "Continuidad",
"complianceRef": "Ley 21.719 Art. 14"
}'| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
questionText | string (2-2000 chars) | Sí | Texto de la pregunta |
questionType | enum | Sí | YES_NO, MULTIPLE_CHOICE, TEXT, SCALE_1_5, DATE |
options | array | Solo MULTIPLE_CHOICE | [{ label, value, weight? }] — weight entre 0 y 1 |
isRequired | boolean | No | Default: true |
weight | integer (0-10) | No | Default: 1 |
orderIndex | integer | No | Posición; si se omite, se añade al final |
section | string (máx. 200) | No | Agrupa preguntas visualmente |
complianceRef | string (máx. 200) | No | Referencia normativa (p. ej. ISO 27001 §8.1) |
Respuesta (201):
{
"success": true,
"data": {
"question": {
"id": "q_new007",
"templateId": "tpl_abc123",
"questionText": "¿Realiza copias de seguridad con frecuencia mínima semanal?",
"questionType": "YES_NO",
"isRequired": true,
"weight": 2,
"orderIndex": 5,
"section": "Continuidad",
"complianceRef": "Ley 21.719 Art. 14"
}
}
}Actualizar pregunta
PUT /questionnaires/templates/:id/questions/:qIdAcepta los mismos campos que la creación. Solo envía los campos que quieras modificar.
Eliminar pregunta
DELETE /questionnaires/templates/:id/questions/:qIdEliminación permanente (hard delete). No se puede deshacer.
Asignaciones
Una asignación envía una plantilla a una persona específica (proveedor, usuario interno o auditor externo) mediante un enlace único de acceso.
Listar asignaciones
GET /questionnaires/assignmentsQuery params opcionales:
| Param | Valores | Descripción |
|---|---|---|
status | PENDING, IN_PROGRESS, PENDING_VALIDATION, COMPLETED, EXPIRED | Filtrar por estado |
templateType | enum de tipos de plantilla | Filtrar por tipo de plantilla |
legalEntityId | UUID | Filtrar por entidad legal |
assignedToMe | true | false | Solo asignaciones del usuario autenticado |
page | integer | Default: 1 |
limit | integer (máx. 100) | Default: 20 |
curl "https://api.dpolab.com/api/v1/questionnaires/assignments?status=PENDING_VALIDATION&limit=10" \
-H "Authorization: Bearer {token}"Respuesta (200):
{
"success": true,
"data": {
"assignments": [
{
"id": "asgn_001",
"templateId": "tpl_abc123",
"templateType": "PROVIDER_EVALUATION",
"templateTitle": "Evaluación de Proveedor — Nivel 1",
"title": "Evaluación Proveedor Tech S.A. — Q2 2026",
"assigneeName": "Juan Pérez",
"assigneeEmail": "[email protected]",
"assigneeType": "PROVIDER",
"dueDate": "2026-06-30",
"status": "PENDING_VALIDATION",
"score": 18,
"maxScore": 24,
"scorePct": 75,
"pendingManualReview": 2,
"totalQuestions": 12,
"answeredCount": 12,
"createdAt": "2026-05-01T09:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 3
}
}
}Crear asignación
POST /questionnaires/assignmentsRequiere rol ANALYST o superior. Al crear la asignación, se envía automáticamente un correo al respondente con el enlace de acceso (si el tenant tiene SMTP configurado).
curl -X POST https://api.dpolab.com/api/v1/questionnaires/assignments \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"templateId": "tpl_abc123",
"title": "Evaluación Proveedor Tech S.A. — Q2 2026",
"assigneeName": "Juan Pérez",
"assigneeEmail": "[email protected]",
"assigneeType": "PROVIDER",
"dueDate": "2026-06-30",
"tokenTtlDays": 30
}'| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
templateId | UUID | Sí | ID de la plantilla (debe estar activa) |
title | string (2-300 chars) | Sí | Nombre de la asignación |
assigneeName | string (2-200 chars) | Sí | Nombre del respondente |
assigneeEmail | Sí | Correo del respondente (cifrado en reposo) | |
assigneeType | enum | Sí | PROVIDER, INTERNAL_USER, EXTERNAL_AUDITOR |
internalUserId | UUID | No | Si es usuario interno del tenant |
legalEntityId | UUID | No | Entidad legal asociada |
dueDate | string YYYY-MM-DD | No | Fecha límite para responder |
tokenTtlDays | integer (1-180) | No | Vigencia del enlace en días (default: 30) |
Respuesta (201):
{
"success": true,
"data": {
"assignment": {
"id": "asgn_new002",
"templateId": "tpl_abc123",
"title": "Evaluación Proveedor Tech S.A. — Q2 2026",
"status": "PENDING",
"dueDate": "2026-06-30"
},
"publicUrl": "https://app.dpolab.com/questionnaire/a1b2c3d4-...",
"emailDelivered": true
}
}El campo publicUrl contiene el enlace único para que el respondente complete el cuestionario. Si emailDelivered es false, el enlace fue generado correctamente pero no se pudo enviar por correo — compártelo manualmente.
Obtener asignación
GET /questionnaires/assignments/:idRetorna la asignación completa con su línea de tiempo de eventos y las respuestas registradas.
curl https://api.dpolab.com/api/v1/questionnaires/assignments/asgn_001 \
-H "Authorization: Bearer {token}"Respuesta (200):
{
"success": true,
"data": {
"assignment": {
"id": "asgn_001",
"status": "PENDING_VALIDATION",
"score": 18,
"maxScore": 24,
"scorePct": 75,
"pendingManualReview": 2,
"completedAt": "2026-05-20T14:32:00Z",
"validatedAt": null
},
"timeline": [
{ "id": "ev_001", "type": "CREATED", "actorId": "usr_xyz", "createdAt": "2026-05-01T09:00:00Z" },
{ "id": "ev_002", "type": "SENT", "actorId": "usr_xyz", "metadata": { "emailDelivered": true }, "createdAt": "2026-05-01T09:00:01Z" },
{ "id": "ev_003", "type": "OPENED", "actorId": null, "createdAt": "2026-05-10T11:00:00Z" },
{ "id": "ev_004", "type": "COMPLETED", "actorId": null, "createdAt": "2026-05-20T14:32:00Z" }
],
"responses": [
{
"id": "resp_001",
"questionId": "q_001",
"value": "yes",
"options": null,
"scorePoints": 3,
"respondedAt": "2026-05-20T14:30:00Z"
}
]
}
}Expirar asignación
PUT /questionnaires/assignments/:id/expireRequiere rol ADMIN. No se puede expirar una asignación ya completada.
curl -X PUT https://api.dpolab.com/api/v1/questionnaires/assignments/asgn_001/expire \
-H "Authorization: Bearer {token}"Respuesta (200):
{
"success": true,
"data": { "id": "asgn_001", "expired": true }
}Validar asignación
POST /questionnaires/assignments/:id/validateRequiere rol ADMIN. Solo aplica a asignaciones en estado PENDING_VALIDATION. Marca la asignación como COMPLETED y registra quién validó y cuándo.
curl -X POST https://api.dpolab.com/api/v1/questionnaires/assignments/asgn_001/validate \
-H "Authorization: Bearer {token}"Respuesta (200):
{
"success": true,
"data": { "id": "asgn_001", "validated": true }
}Estadísticas
GET /questionnaires/statsResumen global de asignaciones del tenant.
curl https://api.dpolab.com/api/v1/questionnaires/stats \
-H "Authorization: Bearer {token}"Respuesta (200):
{
"success": true,
"data": {
"total": 45,
"byStatus": {
"PENDING": 8,
"IN_PROGRESS": 5,
"PENDING_VALIDATION": 3,
"COMPLETED": 27,
"EXPIRED": 2
},
"avgScore": 72.4,
"completionRate": 60
}
}Endpoints públicos (sin autenticación)
Estos endpoints son accedidos directamente por el respondente a través del enlace compartido. Están bajo el prefijo /public/questionnaire/ y no requieren JWT. La autenticación se realiza mediante el token UUID de la asignación incluido en la URL.
Están limitados a 10 solicitudes por minuto por IP y token.
Ver cuestionario
GET /public/questionnaire/:tokenObtiene el cuestionario asociado al token. Si es la primera vez que se accede, la asignación pasa de PENDING a IN_PROGRESS.
Respuesta (200):
{
"success": true,
"data": {
"assignment": {
"title": "Evaluación Proveedor Tech S.A. — Q2 2026",
"templateTitle": "Evaluación de Proveedor — Nivel 1",
"assigneeName": "Juan Pérez",
"dueDate": "2026-06-30",
"status": "IN_PROGRESS"
},
"questions": [
{
"id": "q_001",
"section": "Seguridad",
"questionText": "¿Cuenta con política de seguridad de la información vigente?",
"questionType": "YES_NO",
"options": null,
"isRequired": true,
"orderIndex": 0,
"complianceRef": "ISO 27001 §5.2"
}
],
"readOnly": false
}
}Si el cuestionario ya fue completado o está en revisión, readOnly es true y questions es un arreglo vacío.
Enviar respuestas
POST /public/questionnaire/:token/respondEnvía todas las respuestas de una vez. La plataforma calcula el puntaje automáticamente y pone la asignación en PENDING_VALIDATION.
curl -X POST "https://api.dpolab.com/api/v1/public/questionnaire/a1b2c3d4-.../respond" \
-H "Content-Type: application/json" \
-d '{
"responses": [
{ "questionId": "q_001", "value": "yes" },
{ "questionId": "q_002", "options": ["opcion_a"] },
{ "questionId": "q_003", "value": "Implementamos cifrado AES-256 en todos los sistemas." }
]
}'Respuesta (200):
{
"success": true,
"data": {
"submitted": true,
"score": 18,
"maxScore": 22,
"scorePct": 82,
"pendingManualReview": 1,
"status": "PENDING_VALIDATION"
}
}Si el puntaje es inferior al 70% y la asignación tiene una entidad legal asociada, la plataforma crea automáticamente una tarea de revisión para el equipo de cumplimiento.
Estado de una asignación
| Estado | Descripción |
|---|---|
PENDING | Creada, enlace aún no abierto |
IN_PROGRESS | El respondente abrió el enlace |
PENDING_VALIDATION | Respuestas enviadas, esperando revisión del ADMIN |
COMPLETED | Validada por el ADMIN |
EXPIRED | Expirada manualmente o por vencimiento del token |
Ver también
- Módulo Cuestionarios — Guía funcional con flujos y capturas