Certificado de delitos sexuales (LOPIVI)
Valida el certificado negativo de delitos sexuales de entrenadores y voluntarios (reciente, del titular, sin antecedentes) sin guardarlo.
Si en tu organización hay personas que trabajan con menores (entrenadores, monitores, voluntarios), tienes que pedirles
el certificado negativo del Registro Central de Delincuentes Sexuales. Con el tipo es_sexual_offences_certificate
Constaia comprueba que es ese certificado, que es de esa persona, que no es antiguo y que no constan antecedentes.
Es un dato especialmente sensible, así que esta guía lo procesa sin guardar nada en Constaia.
Esta guía explica cómo automatizar la comprobación, no qué exige la ley en tu caso. La antigüedad máxima que aceptas y cada cuánto lo vuelves a pedir lo decide tu organización con su asesoría jurídica.
Las opciones
{
"expect": "es_sexual_offences_certificate",
"checks": {
"max_age_days": 90,
"holder": { "full_name": "María García López", "document_number": "12345678Z" }
},
"storage": "none",
"keep_results": false,
"metadata": { "volunteer_id": "311" }
}| Opción | Por qué |
|---|---|
expect | Si llega otro documento (por ejemplo, el certificado de antecedentes penales, que es es_criminal_record_certificate), el veredicto es invalid con type_mismatch. |
checks.max_age_days | Antigüedad máxima desde la fecha de emisión. 90 es un ejemplo: pon la ventana que haya fijado tu organización. |
checks.holder | Que el titular del certificado es la persona que tienes registrada. |
storage: "none" | El fichero se procesa en memoria y no se escribe en ningún almacenamiento. |
keep_results: false | Tampoco se guardan los datos extraídos. Recibes la respuesta una vez y después GET /v1/analyses/{id} devuelve 404. |
El motivo has_records se añade siempre, sin pedirlo:
has_records | severity | Mensaje |
|---|---|---|
| El certificado dice que no constan antecedentes | info | "El certificado indica que no constan antecedentes." |
| El certificado dice que constan antecedentes | error → invalid | "El certificado indica que constan antecedentes." |
El código de verificación (CSV)
Los certificados llevan un código seguro de verificación (CSV). Constaia lo extrae en csv_code y ejecuta la
validación csv_format.
csv_format solo comprueba el formato
csv_format comprueba que el código tiene un formato válido. Constaia no consulta al Ministerio de Justicia si el
código existe ni si corresponde a este certificado. Si tu procedimiento exige comprobar la autenticidad, introduce el
CSV en el servicio de verificación de documentos de la sede electrónica del Ministerio de Justicia y compara el
resultado con el documento que te han enviado.
Si el formato no es válido, checks[] trae csv_format con passed: false y el veredicto pasa a invalid con un
motivo csv_format de severidad error.
La firma electrónica del PDF
El certificado que se descarga de la sede del Ministerio de Justicia es un PDF con firma electrónica (sello del
Ministerio). Constaia la verifica sin coste adicional y sin consultar a nadie: que el contenido no se ha tocado desde
que se firmó y que el certificado del firmante llega a una autoridad de confianza. El resultado va en signature y en
verdict.reasons:
| Qué llega | Motivo | Sin require_valid_signature | Con require_valid_signature: true |
|---|---|---|---|
| El PDF original, intacto | signature_valid | info | info |
| Un PDF modificado tras firmarse | document_modified_after_signing | warning → review | error → invalid |
| Una firma rota | signature_invalid | error → invalid | error → invalid |
| Un PDF sin firma, una foto o un escaneo | signature_missing | warning → review si es un PDF; en una foto no se añade | error → invalid |
Si solo aceptas el PDF original, añade "require_valid_signature": true a checks: una foto o un "imprimir a PDF"
pasan a invalid y la persona sabe que tiene que subir el fichero descargado. Si aceptas fotos, déjalo sin activar y
revisa a mano los review. Compara también signature.signer con el organismo esperado: una firma válida solo
acredita quién firmó. Detalles y límites en Firmas digitales en PDF.
El código
import { Constaia, ConstaiaError } from "@constaia/sdk";
import { fromPath } from "@constaia/sdk/node";
const constaia = new Constaia(); // lee CONSTAIA_API_KEY
export async function checkSexualOffencesCertificate(path, person) {
const analysis = await constaia.analyze(await fromPath(path), {
expect: "es_sexual_offences_certificate",
checks: {
maxAgeDays: 90,
holder: { fullName: person.fullName, documentNumber: person.documentNumber },
},
storage: "none",
keepResults: false,
metadata: { volunteer_id: String(person.id) },
});
return {
analysisId: analysis.id,
checkedAt: analysis.completed_at,
status: analysis.verdict?.status ?? "review",
problems: (analysis.verdict?.reasons ?? [])
.filter((r) => r.severity !== "info")
.map((r) => r.message),
};
}
try {
const result = await checkSexualOffencesCertificate("./sexual_offences_certificate.pdf", {
id: 311,
fullName: "María García López",
documentNumber: "12345678Z",
});
console.log(result);
} catch (err) {
if (err instanceof ConstaiaError) console.error(err.code, err.message, err.requestId);
else throw err;
}Qué devuelve
Con el fichero de test sexual_offences_certificate.pdf y las opciones del ejemplo de curl, si el PDF es el original
firmado (si no, verás además signature_missing con warning y el veredicto será review; ver
Probarlo):
{
"expected": ["es_sexual_offences_certificate"],
"match": true,
"status": "valid",
"reasons": [
{ "code": "type_match", "severity": "info", "message": "El documento es Certificado de delitos de naturaleza sexual." },
{ "code": "max_age_days", "severity": "info", "message": "Emitido hace 14 días (máximo 90)." },
{ "code": "holder", "severity": "info", "message": "Los datos del titular coinciden (full_name)." },
{ "code": "has_records", "severity": "info", "message": "El certificado indica que no constan antecedentes." }
]
}Y en checks[]: nif_check_digit (la letra del DNI del titular, 12345678Z) y csv_format con passed: true. Los campos
son holder_name (MARÍA GARCÍA LÓPEZ), holder_id (12345678Z), issue_date (2026-09-15), has_records (false)
y csv_code (MJU4-7K2P-9QXA-3ZTR). Los días de "Emitido hace…" dependen de la fecha en que lo ejecutes.
Qué guardar en tu sistema
Como Constaia no conserva nada (keep_results: false), tu registro es la única constancia. Guarda lo mínimo:
| Dato | Para qué |
|---|---|
analysis.id | Referencia del análisis ante una incidencia o una consulta a soporte. No podrás recuperar el resultado con él. |
Fecha de la comprobación (completed_at) | Saber cuándo caduca según tu ventana y cuándo volver a pedirlo. |
Resultado (valid, invalid, review) | La decisión tomada. |
No guardes el PDF ni el resto de campos si no los necesitas. Si guardas el csv_code para verificarlo más tarde en la
sede electrónica, trátalo como un dato sensible: sirve para localizar el certificado.
Decidir
| Veredicto | Acción |
|---|---|
| Válido | Registra la fecha y habilita a la persona para actividades con menores. |
| No válido | Si el motivo es has_records, sigue tu protocolo interno. Si es antigüedad, titular o tipo, pide un certificado nuevo. |
| Revisar | Revisión humana: foto mala, fecha de emisión ilegible o confianza baja. |
Para review necesitas tu propia copia del documento, porque con storage: "none" Constaia no la conserva. Lo explica
Revisión humana.
Probarlo
Con una clave ck_test_…, un PDF llamado sexual_offences_certificate.pdf (o cualquier nombre que contenga sexual,
delitos o penales) devuelve el certificado sin antecedentes de MARÍA GARCÍA LÓPEZ emitido el 2026-09-15. Cambia
holder.full_name para ver invalid o baja max_age_days a 7 para ver el motivo max_age_days con error. Más en
Modo test.
La firma no se simula: con un PDF cualquiera sin firmar recibirás signature_missing (warning) y el veredicto
review. Usa una imagen JPEG o PNG con ese nombre para ver el valid de la tabla, o tu propio certificado descargado de
la sede para ver signature_valid.
Siguientes pasos
Certificado médico deportivo
Comprueba que un certificado médico deportivo declara apto, está firmado y sellado, es del deportista y no supera la antigüedad máxima que fijes.
Conciliar justificantes de transferencia
Comprueba que cada justificante de transferencia trae el importe, el IBAN de tu cuenta y la referencia, uno a uno o en lotes con export a Excel.