Ir al contenido

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ámetroTipoPor 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.integer50
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
CampoTipoAdmite nulo
idstringno
lastNamestringno
contactFirstNamestringsí
contactEmailstringno
contactPhonestringsí
addressstringsí
parent2FirstNamestringsí
parent2LastNamestringsí
parent2Emailstringsí
parent2Phonestringsí
emailOptInbooleanno
smsOptInbooleanno
emailCommercialOptOutAtstringsí
smsOptOutAtstringsí
archivedAtstringsí
mergedIntoIdstringsí
createdAtstringno
updatedAtstringno

/api/v1/eleves

Demande la portée « eleves:lecture ».

/api/v1/eleves
CampoTipoAdmite nulo
idstringno
familyIdstringno
firstNamestringno
lastNamestringno
birthDatestringsí
isAdultbooleanno
competitivebooleanno
photoConsentAtstringsí
photoConsentWithdrawnAtstringsí
createdAtstringno
updatedAtstringno

/api/v1/cours

Demande la portée « cours:lecture ».

/api/v1/cours
CampoTipoAdmite nulo
idstringno
sessionIdstringno
namestringno
descriptionstringsí
registrationEmailNotestringsí
styleIdstringno
levelIdstringsí
stream"RECREATIONAL" | "COMPETITIVE" | "BOTH" | "ADULT_RECREATIONAL" | "ADULT_COMPETITIVE" | "ADULT_BOTH"no
minAgenumbersí
maxAgenumbersí
ageCutoffMonthnumbersí
ageCutoffDaynumbersí
capacitynumberno
pricestringno
taxExemptbooleanno
instructorIdstringsí
assistantIdsstring[]no
coInstructorIdsstring[]no
roomIdstringsí
trialsOpenbooleanno
approvalRequiredbooleanno
slotsobject[]no
createdAtstringno
updatedAtstringno

/api/v1/inscriptions

Demande la portée « inscriptions:lecture ».

/api/v1/inscriptions
CampoTipoAdmite nulo
idstringno
studentIdstringno
classIdstringno
status"ENROLLED" | "WAITLISTED" | "CANCELLED" | "CART"no
enrolledAtstringno
paymentIdstringsí
ageOverridebooleanno
prereqOverridebooleanno
offeredAtstringsí
offerExpiresAtstringsí
updatedAtstringno

/api/v1/presences

Demande la portée « presences:lecture ».

/api/v1/presences
CampoTipoAdmite nulo
idstringno
enrollmentIdstringno
datestringno
status"PRESENT" | "ABSENT" | "LATE" | "EXCUSED"no
createdAtstringno
updatedAtstringno

/api/v1/paiements

Demande la portée « paiements:lecture ».

/api/v1/paiements
CampoTipoAdmite nulo
idstringno
familyIdstringno
invoiceNumberstringsí
descriptionstringsí
amountstringno
status"PENDING" | "PAID" | "REFUNDED" | "FAILED"no
method"CASH" | "INTERAC" | "CARD" | "CHEQUE" | "OTHER" | "CHEQUE_VACANCES" | "COUPON_SPORT" | "PASS_SPORT" | "PASS_CULTURE" | "CREDIT" | "BANK"sí
dueDatestringsí
paidAtstringsí
processingSincestringsí
createdAtstringno
updatedAtstringno

/api/v1/evenements

Demande la portée « evenements:lecture » ET la capacité « EVENEMENTS » en plus de l'API publique.

/api/v1/evenements
CampoTipoAdmite nulo
idstringno
sessionIdstringno
roomIdstringsí
namestringno
descriptionstringsí
startDatestringno
endDatestringno
joursstring[]no
dailyStartTimestringsí
dailyEndTimestringsí
minAgenumbersí
maxAgenumbersí
capacitynumberno
pricestringno
typesBilletsobject[]no
priceIncludesTaxbooleanno
registrationOpenbooleanno
createdAtstringno
updatedAtstringno

/api/v1/camps

Demande la portée « camps:lecture » ET la capacité « CAMPS » en plus de l'API publique.

/api/v1/camps
CampoTipoAdmite nulo
idstringno
sessionIdstringno
roomIdstringsí
namestringno
descriptionstringsí
startDatestringno
endDatestringno
dailyStartTimestringsí
dailyEndTimestringsí
minAgenumbersí
maxAgenumbersí
capacitynumberno
pricestringno
taxExemptbooleanno
registrationOpenbooleanno
createdAtstringno
updatedAtstringno

/api/v1/representations

Demande la portée « representations:lecture » ET la capacité « BILLETTERIE » en plus de l'API publique.

/api/v1/representations
CampoTipoAdmite nulo
idstringno
showIdstringno
datestringno
venuestringsí
venueRoomIdstringsí
venueMapIdstringsí
ticketPricestringsí
ticketsOnSalebooleanno
ticketsOnSaleAtstringsí
earlyTicketsOnSaleAtstringsí
maxTicketsPerFamilynumbersí
updatedAtstringno

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
CampoTipoObligatorio
lastNamestringsí
contactEmailstringsí
contactFirstNamestringno
contactPhonestringno
addressstringno
parent2FirstNamestringno
parent2LastNamestringno
parent2Emailobjectno
parent2Phonestringno

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
CampoTipoObligatorio
familyIdstringsí
firstNamestringsí
lastNamestringsí
birthDatestringno
isAdultbooleanno
competitivebooleanno

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
CampoTipoObligatorio
studentIdstringsí
classIdstringsí

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
CampoTipoObligatorio
enrollmentIdstringsí
datestringsí
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
CampoTipoObligatorio
familyIdstringsí
amountnumbersí
method"CASH" | "INTERAC" | "CARD" | "CHEQUE" | "OTHER" | "CHEQUE_VACANCES" | "COUPON_SPORT" | "PASS_SPORT" | "PASS_CULTURE"no
descriptionstringno
status"PENDING" | "PAID"no
dueDatestringno

Errores

Todo error devuelve la misma forma: «error.code» y «error.message». El código es contractual; el mensaje puede reformularse.

CódigoEstadoCuándo
MISSING_KEY401Aucun en-tête « Authorization: Bearer … » n'accompagne la requête.
INVALID_KEY401La clé est inconnue ou révoquée. Les deux cas rendent le même message.
STUDIO_SUSPENDED403Le compte du studio est suspendu.
STUDIO_ONBOARDING403La configuration initiale du studio n'est pas terminée.
PLAN_REQUIRED403Le palier du studio n'inclut pas l'API publique, ou pas la capacité que demande la collection visée.
QUOTA_UNAVAILABLE503Le 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_LIMITED429Quota par minute ou quotidien dépassé. L'en-tête « Retry-After » donne le délai en secondes.
SCOPE_REQUIRED403La clé est valide mais ne porte pas la portée qu'exige la route.
INVALID_PARAMETER400Un 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_REQUIRED400L'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_REUSED409Cette 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_PROGRESS409Une é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_BODY400Le 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_LARGE413Le 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_FOUND404Le « familyId » visé n'existe pas dans ce studio.
STUDENT_NOT_FOUND404Le « studentId » visé n'existe pas dans ce studio.
CLASS_NOT_FOUND404Le « classId » visé n'existe pas dans ce studio.
ENROLLMENT_NOT_FOUND404L'« enrollmentId » visé n'existe pas dans ce studio.
ALREADY_ENROLLED409Cet é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_ENROLLABLE409La session du cours n'accepte pas d'inscription : inactive, terminée, ou dont la période est passée.
SCHEDULE_CONFLICT409L'é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_LIMIT409L'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_FUTURE409La 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_CLOSED409Le studio est fermé ce jour-là : aucune séance n'y a lieu.
SESSION_NOT_HELD409La 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_OFFERED422Le 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_ERROR500Erreur 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
EventoCampos de «data»Estado
inscription.creeeinscriptionId, eleveId, coursId, familleId, statut, paiementIdse envía
paiement.recupaiementId, familleId, montant, moyen, numeroFacture, payeLese envía
absence.signaleeinscriptionId, eleveId, coursId, date, statutse envía
famille.creeefamilleId, nom, courrielse envía
billet.venducommandeId, representationId, familleId, quantite, surLesGenoux, paiementId, venduLese 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.