API pública
Una API para conectar Cadanse con el resto de sus herramientas — hojas de cálculo, contabilidad, boletines, automatización. Todo lo que sale se lee; lo que entra se escribe con una clave de idempotencia. Una clave por escuela, una cabecera, JSON.
El texto de referencia siguiente — descripciones de campos, parámetros y condiciones de error — procede directamente del contrato y está escrito en francés, igual que la propia API. Los nombres de campos y de códigos son los que verá en sus propias respuestas.
El contrato
El documento OpenAPI 3.0 se sirve sin clave y se genera desde el código que responde: no puede describir nada distinto de lo que la API devuelve realmente. Es lo que hay que entregar a un generador de clientes.
Abrir openapi.json
Empezar
Cree una clave en el panel, en Configuración. Su secreto se muestra una sola vez — cópielo enseguida; Cadanse solo guarda una huella y no puede volver a mostrarlo.
Compruébela en la ruta de identidad, que no exige ningún alcance y devuelve su escuela, sus alcances y su cuota restante:
curl -H "Authorization: Bearer cad_..." \
https://cadanse.app/api/v1/moi
Después lea una colección. Cada respuesta lleva «data» y «next»; devuelva «next» como «apres» hasta que sea nulo.
curl -H "Authorization: Bearer cad_..." \
"https://cadanse.app/api/v1/familles?limite=100"
120 peticiones por minuto y 20000 por día, por clave. Superarlas devuelve 429 con la cabecera «Retry-After». El contador está cerrado por defecto: si no responde, la petición se rechaza en vez de dejarse pasar.
Paginación y filtro
Los tres parámetros que acepta cada colección.
| Parámetro | Tipo | Por defecto |
|---|
| apresCurseur exclusif : le dernier identifiant reçu à la page précédente, c'est-à-dire la valeur de « next ». Absent, la lecture commence au début. | string | — |
|---|
| limiteNombre de lignes par page. Une valeur supérieure à 200 est ramenée à 200 sans erreur ; une valeur non entière ou nulle est refusée. | integer | 50 |
|---|
| modifieDepuisNe rend que les lignes dont « updatedAt » est postérieur ou égal à cet instant ISO 8601. Une date invalide est REFUSÉE, jamais ignorée : un filtre qui disparaît en silence fait relire toute la collection à chaque sondage. | string | — |
|---|
Colecciones
Cada una exige el alcance «<recurso>:lecture» (por ejemplo «familles:lecture»; cursos, campamentos, eventos y funciones no exponen datos de contacto de las familias). Los campos indicados son exhaustivos: lo que no aparece no sale de Cadanse.
/api/v1/familles
Demande la portée « familles:lecture ».
/api/v1/familles| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| lastName | string | no |
|---|
| contactFirstName | string | sí |
|---|
| contactEmail | string | no |
|---|
| contactPhone | string | sí |
|---|
| address | string | sí |
|---|
| parent2FirstName | string | sí |
|---|
| parent2LastName | string | sí |
|---|
| parent2Email | string | sí |
|---|
| parent2Phone | string | sí |
|---|
| emailOptIn | boolean | no |
|---|
| smsOptIn | boolean | no |
|---|
| emailCommercialOptOutAt | string | sí |
|---|
| smsOptOutAt | string | sí |
|---|
| archivedAt | string | sí |
|---|
| mergedIntoId | string | sí |
|---|
| createdAt | string | no |
|---|
| updatedAt | string | no |
|---|
/api/v1/eleves
Demande la portée « eleves:lecture ».
/api/v1/eleves| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| familyId | string | no |
|---|
| firstName | string | no |
|---|
| lastName | string | no |
|---|
| birthDate | string | sí |
|---|
| isAdult | boolean | no |
|---|
| competitive | boolean | no |
|---|
| photoConsentAt | string | sí |
|---|
| photoConsentWithdrawnAt | string | sí |
|---|
| createdAt | string | no |
|---|
| updatedAt | string | no |
|---|
/api/v1/cours
Demande la portée « cours:lecture ».
/api/v1/cours| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| sessionId | string | no |
|---|
| name | string | no |
|---|
| description | string | sí |
|---|
| registrationEmailNote | string | sí |
|---|
| styleId | string | no |
|---|
| levelId | string | sí |
|---|
| stream | "RECREATIONAL" | "COMPETITIVE" | "BOTH" | "ADULT_RECREATIONAL" | "ADULT_COMPETITIVE" | "ADULT_BOTH" | no |
|---|
| minAge | number | sí |
|---|
| maxAge | number | sí |
|---|
| ageCutoffMonth | number | sí |
|---|
| ageCutoffDay | number | sí |
|---|
| capacity | number | no |
|---|
| price | string | no |
|---|
| taxExempt | boolean | no |
|---|
| instructorId | string | sí |
|---|
| assistantIds | string[] | no |
|---|
| coInstructorIds | string[] | no |
|---|
| roomId | string | sí |
|---|
| trialsOpen | boolean | no |
|---|
| approvalRequired | boolean | no |
|---|
| slots | object[] | no |
|---|
| createdAt | string | no |
|---|
| updatedAt | string | no |
|---|
/api/v1/inscriptions
Demande la portée « inscriptions:lecture ».
/api/v1/inscriptions| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| studentId | string | no |
|---|
| classId | string | no |
|---|
| status | "ENROLLED" | "WAITLISTED" | "CANCELLED" | "CART" | no |
|---|
| enrolledAt | string | no |
|---|
| paymentId | string | sí |
|---|
| ageOverride | boolean | no |
|---|
| prereqOverride | boolean | no |
|---|
| offeredAt | string | sí |
|---|
| offerExpiresAt | string | sí |
|---|
| updatedAt | string | no |
|---|
/api/v1/presences
Demande la portée « presences:lecture ».
/api/v1/presences| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| enrollmentId | string | no |
|---|
| date | string | no |
|---|
| status | "PRESENT" | "ABSENT" | "LATE" | "EXCUSED" | no |
|---|
| createdAt | string | no |
|---|
| updatedAt | string | no |
|---|
/api/v1/paiements
Demande la portée « paiements:lecture ».
/api/v1/paiements| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| familyId | string | no |
|---|
| invoiceNumber | string | sí |
|---|
| description | string | sí |
|---|
| amount | string | no |
|---|
| status | "PENDING" | "PAID" | "REFUNDED" | "FAILED" | no |
|---|
| method | "CASH" | "INTERAC" | "CARD" | "CHEQUE" | "OTHER" | "CHEQUE_VACANCES" | "COUPON_SPORT" | "PASS_SPORT" | "PASS_CULTURE" | "CREDIT" | "BANK" | sí |
|---|
| dueDate | string | sí |
|---|
| paidAt | string | sí |
|---|
| processingSince | string | sí |
|---|
| createdAt | string | no |
|---|
| updatedAt | string | no |
|---|
/api/v1/evenements
Demande la portée « evenements:lecture » ET la capacité « EVENEMENTS » en plus de l'API publique.
/api/v1/evenements| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| sessionId | string | no |
|---|
| roomId | string | sí |
|---|
| name | string | no |
|---|
| description | string | sí |
|---|
| startDate | string | no |
|---|
| endDate | string | no |
|---|
| jours | string[] | no |
|---|
| dailyStartTime | string | sí |
|---|
| dailyEndTime | string | sí |
|---|
| minAge | number | sí |
|---|
| maxAge | number | sí |
|---|
| capacity | number | no |
|---|
| price | string | no |
|---|
| typesBillets | object[] | no |
|---|
| priceIncludesTax | boolean | no |
|---|
| registrationOpen | boolean | no |
|---|
| createdAt | string | no |
|---|
| updatedAt | string | no |
|---|
/api/v1/camps
Demande la portée « camps:lecture » ET la capacité « CAMPS » en plus de l'API publique.
/api/v1/camps| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| sessionId | string | no |
|---|
| roomId | string | sí |
|---|
| name | string | no |
|---|
| description | string | sí |
|---|
| startDate | string | no |
|---|
| endDate | string | no |
|---|
| dailyStartTime | string | sí |
|---|
| dailyEndTime | string | sí |
|---|
| minAge | number | sí |
|---|
| maxAge | number | sí |
|---|
| capacity | number | no |
|---|
| price | string | no |
|---|
| taxExempt | boolean | no |
|---|
| registrationOpen | boolean | no |
|---|
| createdAt | string | no |
|---|
| updatedAt | string | no |
|---|
/api/v1/representations
Demande la portée « representations:lecture » ET la capacité « BILLETTERIE » en plus de l'API publique.
/api/v1/representations| Campo | Tipo | Admite nulo |
|---|
| id | string | no |
|---|
| showId | string | no |
|---|
| date | string | no |
|---|
| venue | string | sí |
|---|
| venueRoomId | string | sí |
|---|
| venueMapId | string | sí |
|---|
| ticketPrice | string | sí |
|---|
| ticketsOnSale | boolean | no |
|---|
| ticketsOnSaleAt | string | sí |
|---|
| earlyTicketsOnSaleAt | string | sí |
|---|
| maxTicketsPerFamily | number | sí |
|---|
| updatedAt | string | no |
|---|
Escribir
Cada escritura exige el alcance «<recurso>:ecriture» (por ejemplo «inscriptions:ecriture»), y registrar un pago tiene su propio alcance «paiements:ecriture», que se marca aparte. Cada una pasa por la misma regla que la pantalla correspondiente del panel: una inscripción rechazada en pantalla se rechaza también aquí, y el código de error nombra la regla que la rechazó. Los campos indicados son los únicos aceptados; un campo desconocido, o uno que la API no lee, devuelve 400.
La cabecera «Idempotency-Key» es obligatoria: un valor suyo, único por escritura. Reintentar con la misma clave devuelve la respuesta original durante 24 horas, con la cabecera «Idempotent-Replayed: true» — eso es lo que le permite reintentar sin arriesgar una segunda inscripción. La misma clave con un cuerpo distinto devuelve 409; una escritura rechazada, en cambio, libera la clave, para que pueda corregir su envío y reintentar con la misma.
Una creación, y la misma llamada repetida:
curl -X POST -H "Authorization: Bearer cad_..." \
-H "Idempotency-Key: 8f1c-..." \
-H "Content-Type: application/json" \
-d '{"lastName":"…","contactEmail":"…"}' \
https://cadanse.app/api/v1/familles
POST /api/v1/familles
Demande la portée « familles:ecriture » et l'en-tête « Idempotency-Key ». La famille est créée sans élève : un élève se crée par sa propre écriture. La réponse porte la ligne au même format qu'en lecture.
POST /api/v1/familles| Campo | Tipo | Obligatorio |
|---|
| lastName | string | sí |
|---|
| contactEmail | string | sí |
|---|
| contactFirstName | string | no |
|---|
| contactPhone | string | no |
|---|
| address | string | no |
|---|
| parent2FirstName | string | no |
|---|
| parent2LastName | string | no |
|---|
| parent2Email | object | no |
|---|
| parent2Phone | string | no |
|---|
POST /api/v1/eleves
Demande la portée « eleves:ecriture » et l'en-tête « Idempotency-Key ». L'élève est rattaché à une famille existante. Ni note médicale ni note interne : aucune des deux ne se lit par l'API, et ce qui ne se lit pas ne s'écrit pas.
POST /api/v1/eleves| Campo | Tipo | Obligatorio |
|---|
| familyId | string | sí |
|---|
| firstName | string | sí |
|---|
| lastName | string | sí |
|---|
| birthDate | string | no |
|---|
| isAdult | boolean | no |
|---|
| competitive | boolean | no |
|---|
POST /api/v1/inscriptions
Demande la portée « inscriptions:ecriture » et l'en-tête « Idempotency-Key ». Passe par la même fonction de domaine que le tableau de bord : capacité, conflits d'horaire, limites par participant et inscriptibilité de la session peuvent la refuser, et l'erreur nomme alors la règle. Un cours complet met l'élève en LISTE D'ATTENTE — c'est un succès, et le champ « status » de la réponse le dit. La facture de la session est émise comme elle le serait au tableau de bord.
POST /api/v1/inscriptions| Campo | Tipo | Obligatorio |
|---|
| studentId | string | sí |
|---|
| classId | string | sí |
|---|
POST /api/v1/presences
Demande la portée « presences:ecriture » et l'en-tête « Idempotency-Key ». Marque une séance d'une inscription, aux mêmes conditions que la feuille du tableau de bord : jamais d'avance, jamais un jour de fermeture, jamais une séance annulée ou déplacée. Un statut « EXCUSED » émet un crédit de reprise quand le studio en accorde ; une transition vers « ABSENT » prévient la famille et émet l'événement « absence.signalee ». Remarquer la même séance met la marque à jour sans rien réémettre.
POST /api/v1/presences| Campo | Tipo | Obligatorio |
|---|
| enrollmentId | string | sí |
|---|
| date | string | sí |
|---|
| status | "PRESENT" | "ABSENT" | "LATE" | "EXCUSED" | sí |
|---|
POST /api/v1/paiements
Demande la portée « paiements:ecriture » et l'en-tête « Idempotency-Key ». Le montant envoyé EST le total, taxes comprises : c'est la saisie de comptoir, pas un encaissement par processeur. La facture reçoit son numéro et son empreinte fiscale, et la famille en est avertie au portail — relance courriel comprise si elle reste due.
POST /api/v1/paiements| Campo | Tipo | Obligatorio |
|---|
| familyId | string | sí |
|---|
| amount | number | sí |
|---|
| method | "CASH" | "INTERAC" | "CARD" | "CHEQUE" | "OTHER" | "CHEQUE_VACANCES" | "COUPON_SPORT" | "PASS_SPORT" | "PASS_CULTURE" | no |
|---|
| description | string | no |
|---|
| status | "PENDING" | "PAID" | no |
|---|
| dueDate | string | no |
|---|
Errores
Todo error devuelve la misma forma: «error.code» y «error.message». El código es contractual; el mensaje puede reformularse.
| Código | Estado | Cuándo |
|---|
| MISSING_KEY | 401 | Aucun en-tête « Authorization: Bearer … » n'accompagne la requête. |
|---|
| INVALID_KEY | 401 | La clé est inconnue ou révoquée. Les deux cas rendent le même message. |
|---|
| STUDIO_SUSPENDED | 403 | Le compte du studio est suspendu. |
|---|
| STUDIO_ONBOARDING | 403 | La configuration initiale du studio n'est pas terminée. |
|---|
| PLAN_REQUIRED | 403 | Le palier du studio n'inclut pas l'API publique, ou pas la capacité que demande la collection visée. |
|---|
| QUOTA_UNAVAILABLE | 503 | Le compteur de quota est momentanément injoignable ; la requête est refusée plutôt que laissée passer. Réessayez après le délai de « Retry-After ». |
|---|
| RATE_LIMITED | 429 | Quota par minute ou quotidien dépassé. L'en-tête « Retry-After » donne le délai en secondes. |
|---|
| SCOPE_REQUIRED | 403 | La clé est valide mais ne porte pas la portée qu'exige la route. |
|---|
| INVALID_PARAMETER | 400 | Un paramètre de requête est mal formé — « limite » non entière ou nulle, « modifieDepuis » qui n'est pas une date ISO 8601. |
|---|
| IDEMPOTENCY_KEY_REQUIRED | 400 | L'en-tête « Idempotency-Key » manque, est vide, ou dépasse 255 caractères. Il est obligatoire sur toute écriture : un connecteur qui réessaie est le cas normal, pas l'exception. |
|---|
| IDEMPOTENCY_KEY_REUSED | 409 | Cette clé a déjà servi, pour une requête DIFFÉRENTE. Rendre la réponse d'origine ferait croire à l'appelant qu'il vient de créer ce que décrit une réponse qui parle d'autre chose. Une clé vaut pour une écriture et une seule. |
|---|
| IDEMPOTENCY_IN_PROGRESS | 409 | Une écriture portant cette clé court encore. Réessayez après le délai de « Retry-After » : la réponse d'origine sera alors rendue. |
|---|
| INVALID_BODY | 400 | Le corps n'est pas du JSON valide, ou un champ ne satisfait pas le schéma de l'écriture. Le message nomme le champ fautif. Le refus ne consomme PAS la clé d'idempotence : corrigez et réessayez avec la même. |
|---|
| PAYLOAD_TOO_LARGE | 413 | Le corps de la requête dépasse la taille permise ; il est refusé avant tout décodage. Réduisez-le (par lots plus petits) avant de réessayer. |
|---|
| FAMILY_NOT_FOUND | 404 | Le « familyId » visé n'existe pas dans ce studio. |
|---|
| STUDENT_NOT_FOUND | 404 | Le « studentId » visé n'existe pas dans ce studio. |
|---|
| CLASS_NOT_FOUND | 404 | Le « classId » visé n'existe pas dans ce studio. |
|---|
| ENROLLMENT_NOT_FOUND | 404 | L'« enrollmentId » visé n'existe pas dans ce studio. |
|---|
| ALREADY_ENROLLED | 409 | Cet élève est déjà inscrit à ce cours. Réinscrire ne crée pas de seconde ligne — le refus est ce qui empêche une double facturation. |
|---|
| SESSION_NOT_ENROLLABLE | 409 | La session du cours n'accepte pas d'inscription : inactive, terminée, ou dont la période est passée. |
|---|
| SCHEDULE_CONFLICT | 409 | L'élève a déjà un cours qui chevauche celui-ci. Seul le studio peut assumer un conflit d'horaire, depuis son tableau de bord : l'API ne porte pas cette dérogation. |
|---|
| PARTICIPANT_LIMIT | 409 | L'inscription dépasse une limite fixée par le studio — nombre de cours par élève, ou un seul cours par discipline. |
|---|
| ATTENDANCE_DATE_IN_FUTURE | 409 | La date de la présence est encore à venir. Une présence ne se prend pas d'avance : elle nourrit le décompte de séances tenues et l'avis d'absence à la famille. |
|---|
| STUDIO_CLOSED | 409 | Le studio est fermé ce jour-là : aucune séance n'y a lieu. |
|---|
| SESSION_NOT_HELD | 409 | La séance de ce cours a été annulée ou déplacée : elle n'a pas eu lieu à la date demandée. Le message dit lequel des deux. |
|---|
| METHOD_NOT_OFFERED | 422 | Le moyen de paiement n'existe pas dans le pays du studio : `INTERAC` est propre au Canada, et `BANK` (compte bancaire débité) au Canada (débit préautorisé), aux États-Unis (ACH) et en France (SEPA) ; les Chèques-Vacances, coupons sport, Pass'Sport et Pass Culture sont propres à la France. |
|---|
| INTERNAL_ERROR | 500 | Erreur interne. Le détail est consigné côté Cadanse, jamais rendu à l'appelant. |
|---|
Webhooks salientes
La API se lee y se escribe por iniciativa suya; los webhooks, en cambio, van hacia usted. Un destino se conecta desde el panel, en Configuración: una dirección HTTPS, los eventos suscritos y un secreto de firma que se muestra una sola vez. Cadanse envía allí un POST «application/json» en cada evento.
Verificar la firma
Cada envío lleva la cabecera «Cadanse-Signature», con la forma «t=<milisegundos>,v1=<hex>»: «v1» es el HMAC-SHA256 del texto «<t>.<cuerpo en bruto>», con su secreto como clave. Verifique el cuerpo EN BRUTO, antes de deserializarlo. Compruebe primero la firma y solo después la marca de tiempo: rechazar por antigüedad antes de probar la autenticidad revela a quien le escribe qué hora acepta. Pasados 5 minutos, rechace — eso es lo que impide reproducir en su sistema un cuerpo captado una vez. La cabecera «Cadanse-Event» nombra el evento, para enrutar sin leer el cuerpo.
Cadanse-Signature: t=1776171600000,v1=<hex>
Cadanse-Event: inscription.creee
El sobre
Siempre la misma forma: «id», «type», «createdAt», «studioId» y «data». «id» es el de la ENTREGA, no el del objeto: los reintentos de un mismo envío lo repiten — es lo que le permite eliminar duplicados — y una reproducción manual lleva uno nuevo. «createdAt» es el instante del evento; ninguna carga lleva una segunda marca de tiempo. Los campos de «data» son identificadores: las fichas completas se leen en las colecciones anteriores.
Reintentos
Una respuesta 2xx cierra la entrega. Si no, 3 reintentos, a las 1 h, 6 h, 24 h, y después nada: un evento entregado tres días tarde ya no informa a nadie. La ventana total es de 48 h. Responda rápido — una llamada se abandona pasados 2 segundos — y haga su trabajo después de responder. El panel muestra cada entrega, su código de respuesta y el mensaje de error de su servidor; una entrega agotada se reproduce allí a mano.
Los eventos
Suscribir un evento que todavía no envía nada está permitido, y la columna de la derecha lo dice: es el estado real, leído en el código que emite.
Los eventos| Evento | Campos de «data» | Estado |
|---|
| inscription.creee | inscriptionId, eleveId, coursId, familleId, statut, paiementId | se envía |
|---|
| paiement.recu | paiementId, familleId, montant, moyen, numeroFacture, payeLe | se envía |
|---|
| absence.signalee | inscriptionId, eleveId, coursId, date, statut | se envía |
|---|
| famille.creee | familleId, nom, courriel | se envía |
|---|
| billet.vendu | commandeId, representationId, familleId, quantite, surLesGenoux, paiementId, venduLe | se envía |
|---|
Conectores
Zapier, Make y los asistentes que hablan MCP se conectan a todo lo anterior, y a nada más: la misma clave, los mismos alcances, la misma cuota, el mismo registro. Un conector no abre una puerta más — pasa por una puerta que usted ya abrió, y la clave se revoca desde el panel.
Zapier y Make
Nada que instalar: cree una conexión «API por clave» con la cabecera «Authorization» y, como valor, «Bearer» seguido de su clave. Pruébela en https://cadanse.app/api/v1/moi — esa ruta responde cualesquiera sean los alcances de la clave y devuelve el nombre del estudio y la lista de lo que permite.
Sus disparadores son los webhooks de arriba. Tanto Zapier como Make le dan una URL de escucha: péguela en el panel y marque los eventos que quiera. Ninguno verifica la firma por usted — hágala verificar por un paso de código antes de actuar sobre una carga, con la receta indicada más arriba.
Sus acciones son las escrituras de arriba, llamadas desde un paso de «solicitud HTTP». Rellene siempre «Idempotency-Key»: de lo contrario, un escenario que la plataforma reproduce tras una avería crearía dos veces la misma ficha.
Servidor MCP
Un asistente que habla MCP se conecta a https://cadanse.app/api/v1/mcp — transporte HTTP, solicitudes POST, la misma cabecera «Authorization». Decide su estudio: conectar un asistente hace salir datos hacia una herramienta que usted elige, bajo una clave que usted crea y puede revocar.
{
"mcpServers": {
"cadanse": {
"type": "http",
"url": "https://cadanse.app/api/v1/mcp",
"headers": {
"Authorization": "Bearer cad_..."
}
}
}
}
El servidor expone 9 herramientas de lectura y 5 de escritura — exactamente las colecciones y las escrituras documentadas arriba, ninguna otra. El nombre de una herramienta es el de su operación, en minúsculas con guiones bajos:
lister_familles, lister_eleves, lister_cours, lister_inscriptions, lister_presences, lister_paiements, lister_evenements, lister_camps, lister_representations, creer_famille, creer_eleve, creer_inscription, marquer_presence, creer_paiement
El catálogo depende de la clave: «tools/list» solo devuelve las herramientas que cubren sus alcances. Una herramienta fuera de alcance no existe para esa clave — llamarla responde «herramienta desconocida», lo que evita enseñarle a un curioso qué alcances existen.
Los lotes JSON-RPC se rechazan: una llamada es una solicitud, y una solicitud es una unidad de cuota. Las lecturas hechas por MCP se registran como las demás — una ventana por clave, por colección y por día — en el mismo registro que el estudio puede releer.
Las herramientas de escritura actúan de inmediato, y lo que escriben lo ven el personal y las familias. Cada una acepta un argumento «cleIdempotence» opcional: sin él, dos llamadas idénticas hacen dos escrituras — es deliberado, dos pagos en efectivo del mismo importe el mismo sábado son dos pagos. El registro de auditoría conserva cada escritura, venga de un asistente o de una llamada HTTP.
Por qué esta página no puede mentir
Se genera desde el mismo documento que openapi.json, que a su vez se genera desde los serializadores que escriben las respuestas. Un campo retirado del código desaparece de aquí sin que nadie lo piense; un campo documentado que no se sirve no se puede escribir.