API REST (Desarrolladores)
Cuestionarios

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étodoEndpointRol mínimoDescripción
GET/questionnaires/statsVIEWEREstadísticas globales de asignaciones
GET/questionnaires/templatesVIEWERListar plantillas
POST/questionnaires/templatesADMINCrear plantilla
GET/questionnaires/templates/:idVIEWERObtener plantilla con preguntas
PUT/questionnaires/templates/:idADMINActualizar plantilla
DELETE/questionnaires/templates/:idADMINEliminar plantilla (soft delete)
POST/questionnaires/templates/:id/questionsADMINAgregar pregunta a plantilla
PUT/questionnaires/templates/:id/questions/:qIdADMINActualizar pregunta
DELETE/questionnaires/templates/:id/questions/:qIdADMINEliminar pregunta
GET/questionnaires/assignmentsVIEWERListar asignaciones
POST/questionnaires/assignmentsANALYST+Crear asignación
GET/questionnaires/assignments/:idVIEWERObtener asignación con respuestas
PUT/questionnaires/assignments/:id/expireADMINExpirar asignación
POST/questionnaires/assignments/:id/validateADMINValidar asignación completada
GET/public/questionnaire/:tokenPúblicoVer cuestionario (respondente)
POST/public/questionnaire/:token/respondPúblicoEnviar respuestas

Plantillas

Listar plantillas

GET /questionnaires/templates

Query params opcionales:

ParamValoresDescripción
templateTypePROVIDER_EVALUATION, INTERNAL_AUDIT, DPIA_SCREENING, BREACH_ASSESSMENT, CUSTOMFiltrar por tipo
includeDeletedtrue | falseIncluir 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/templates

Requiere 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"
}
CampoTipoRequeridoDescripción
titlestring (2-300 chars)Nombre de la plantilla
descriptionstring (máx. 2000 chars)NoDescripción de propósito
templateTypeenumTipo: 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/:id

Retorna 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/:id

Requiere 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/:id

Requiere 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

TipoDescripciónPuntuación automática
YES_NOSí/Noyes = weight, resto = 0
SCALE_1_5Escala del 1 al 5(valor / 5) × weight
MULTIPLE_CHOICEOpción única o múltiple con pesosMejor peso seleccionado × weight
DATEFechaPresencia = weight, ausencia = 0
TEXTTexto libreSin 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/questions

Requiere 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"
  }'
CampoTipoRequeridoDescripción
questionTextstring (2-2000 chars)Texto de la pregunta
questionTypeenumYES_NO, MULTIPLE_CHOICE, TEXT, SCALE_1_5, DATE
optionsarraySolo MULTIPLE_CHOICE[{ label, value, weight? }]weight entre 0 y 1
isRequiredbooleanNoDefault: true
weightinteger (0-10)NoDefault: 1
orderIndexintegerNoPosición; si se omite, se añade al final
sectionstring (máx. 200)NoAgrupa preguntas visualmente
complianceRefstring (máx. 200)NoReferencia 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/:qId

Acepta los mismos campos que la creación. Solo envía los campos que quieras modificar.

Eliminar pregunta

DELETE /questionnaires/templates/:id/questions/:qId

Eliminació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/assignments

Query params opcionales:

ParamValoresDescripción
statusPENDING, IN_PROGRESS, PENDING_VALIDATION, COMPLETED, EXPIREDFiltrar por estado
templateTypeenum de tipos de plantillaFiltrar por tipo de plantilla
legalEntityIdUUIDFiltrar por entidad legal
assignedToMetrue | falseSolo asignaciones del usuario autenticado
pageintegerDefault: 1
limitinteger (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/assignments

Requiere 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
  }'
CampoTipoRequeridoDescripción
templateIdUUIDID de la plantilla (debe estar activa)
titlestring (2-300 chars)Nombre de la asignación
assigneeNamestring (2-200 chars)Nombre del respondente
assigneeEmailemailCorreo del respondente (cifrado en reposo)
assigneeTypeenumPROVIDER, INTERNAL_USER, EXTERNAL_AUDITOR
internalUserIdUUIDNoSi es usuario interno del tenant
legalEntityIdUUIDNoEntidad legal asociada
dueDatestring YYYY-MM-DDNoFecha límite para responder
tokenTtlDaysinteger (1-180)NoVigencia 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/:id

Retorna 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/expire

Requiere 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/validate

Requiere 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/stats

Resumen 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/:token

Obtiene 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/respond

Enví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

EstadoDescripción
PENDINGCreada, enlace aún no abierto
IN_PROGRESSEl respondente abrió el enlace
PENDING_VALIDATIONRespuestas enviadas, esperando revisión del ADMIN
COMPLETEDValidada por el ADMIN
EXPIREDExpirada manualmente o por vencimiento del token

Ver también