Constaia
Guías por caso

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.

Muchas federaciones, clubes y organizadores piden un certificado médico que declare a la persona apta para la práctica deportiva. Revisarlos a mano es lento: hay que mirar la fecha, la firma, el sello, el nombre y que diga "apto".

Con el tipo medical_certificate_sport Constaia extrae esos datos y aplica las reglas que le pidas.

Las opciones

options.json
{
  "expect": "medical_certificate_sport",
  "checks": {
    "max_age_days": 365,
    "require_signature": true,
    "require_stamp": true,
    "holder": { "full_name": "María García López", "document_number": "12345678Z" }
  },
  "storage": "none",
  "metadata": { "athlete_id": "5521", "season": "2026-27" }
}
CheckQué compruebaSi falla
max_age_daysQue la fecha de emisión no tiene más de N días.max_age_days con severity: "error". Si no se encuentra la fecha, warning (→ review).
require_signatureQue se aprecia una firma (signature_present).require_signature con error.
require_stampQue se aprecia un sello (stamp_present).require_stamp con error.
holderQue el paciente es el deportista (nombre y documento).holder con error, o warning si no se puede leer el dato.

Además, sin que lo pidas, si el certificado no declara apto (fit_for_sport: false) el veredicto es invalid con el motivo not_fit_for_sport: "El certificado no declara apto para la práctica deportiva.".

Si necesitas que aparezca un dato concreto, como el número de colegiado, añade "require_fields": ["doctor_license_number"]: si falta, llega el motivo required_field_missing con error.

La antigüedad y la temporada

max_age_days cuenta los días entre la fecha de emisión y la fecha de referencia, que por defecto es hoy. Tu organización decide la ventana: 365 días es habitual, pero hay reglamentos que piden el certificado de la temporada en curso.

Si quieres que el certificado siga dentro de plazo al final de la temporada, pasa la fecha de fin en reference_date:

{ "max_age_days": 365, "reference_date": "2027-08-31" }

Un certificado emitido más de un día después de reference_date también falla el check, así que no uses la fecha de inicio de temporada como referencia si aceptas certificados emitidos durante ella.

El código

check-medical.js
import { Constaia, ConstaiaError } from "@constaia/sdk";
import { fromPath } from "@constaia/sdk/node";

const constaia = new Constaia(); // lee CONSTAIA_API_KEY

export async function checkMedicalCertificate(path, athlete) {
  const analysis = await constaia.analyze(await fromPath(path), {
    expect: "medical_certificate_sport",
    checks: {
      maxAgeDays: 365,
      requireSignature: true,
      requireStamp: true,
      holder: { fullName: athlete.fullName, documentNumber: athlete.documentNumber },
    },
    storage: "none",
    metadata: { athlete_id: String(athlete.id), season: "2026-27" },
  });

  const { fields, verdict } = analysis;
  return {
    analysisId: analysis.id,
    status: verdict?.status ?? "review",
    problems: (verdict?.reasons ?? []).filter((r) => r.severity !== "info").map((r) => r.message),
    issueDate: fields.issue_date?.value ?? null,
    restrictions: fields.restrictions?.value ?? null,
    doctorLicenseNumber: fields.doctor_license_number?.value ?? null,
  };
}

try {
  const result = await checkMedicalCertificate("./medical_certificate.pdf", {
    id: 5521,
    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 medical_certificate.pdf y las opciones del ejemplo de curl, el veredicto es:

verdict
{
  "expected": ["medical_certificate_sport"],
  "match": true,
  "status": "valid",
  "reasons": [
    { "code": "type_match", "severity": "info", "message": "El documento es Certificado médico deportivo." },
    { "code": "max_age_days", "severity": "info", "message": "Emitido hace 28 días (máximo 365)." },
    { "code": "require_signature", "severity": "info", "message": "El documento está firmado." },
    { "code": "require_stamp", "severity": "info", "message": "El documento tiene sello." }
  ]
}

Los días de "Emitido hace…" dependen de la fecha en que lo ejecutes. Los campos extraídos (cada uno es un objeto con value, confidence, validated y source):

CampoValor de ejemplo
patient_nameMARÍA GARCÍA LÓPEZ
patient_id12345678Z (con la validación nif_check_digit)
issue_date2026-09-01
doctor_nameLAURA MARTÍN RUIZ
doctor_license_number282845678
fit_for_sporttrue
sportTiro con arco
restrictionsSin restricciones
signature_present, stamp_presenttrue

restrictions es texto libre: si el certificado declara apto con limitaciones (por ejemplo, "no apto para competición de alto nivel"), decide en tu código qué hacer. Constaia no interpreta las restricciones más allá de fit_for_sport.

Lo que Constaia no comprueba

Constaia lee el número de colegiado, pero no consulta al colegio de médicos si ese número existe ni si el médico está colegiado. Tampoco verifica la firma electrónica del PDF. Si tu reglamento lo exige, haz esa comprobación aparte.

Decidir

VeredictoAcción
VálidoMarca el certificado como aceptado y guarda la fecha de emisión para avisar antes de que caduque.
No válidoMuestra los motivos (no apto, antiguo, sin firma o sello, otro titular) y pide otro certificado.
RevisarUna persona lo revisa: falta la fecha, la foto es mala o la confianza es baja. Ver Revisión humana.

Guarda en tu sistema el id del análisis, el veredicto, issue_date y, si lo necesitas, doctor_license_number. Con la fecha de emisión y tu ventana puedes calcular cuándo pedir el siguiente certificado.

Probarlo

Con una clave ck_test_…, un PDF llamado medical_certificate.pdf (también sirve cualquier nombre que contenga medical o medico) devuelve el certificado apto, firmado y sellado, emitido el 2026-09-01, de la página anterior. Con holder { "full_name": "María García López" } añade el motivo holder informativo. Prueba con otro nombre para ver el invalid. Más en Modo test.

Siguientes pasos

En esta página