Desarrolladores
La API de CarelyCloud.
La misma API que usa nuestra aplicación, con una clave en vez de un inicio de sesión. Citas, pacientes, disponibilidad, y webhooks para todo lo que pasa en la clínica. Incluida en los planes Grupo y Enterprise; la prueba gratis puede probarla.
Autenticación
Un administrador crea las claves en Ajustes → Integraciones. La clave se muestra una sola vez. Mándala en cada petición en la cabecera X-API-Key.
curl https://carelycloud-production.up.railway.app/clinics/me \
-H "X-API-Key: cc_live_xxxxxxxxxxxxxxxxxxxx"La clave actúa en nombre de la clínica, no de una persona, y solo alcanza una parte de la API:
- Lectura: citas, pacientes (con notas y archivos), profesionales, disponibilidad, servicios, el perfil de la clínica y las métricas del inicio.
- Escritura: citas y pacientes, incluidas sus notas y archivos.
- Todo lo demás (equipo, ajustes, facturación, integraciones, notas con IA) responde 403 API_KEY_SCOPE.
El acceso se comprueba en cada petición contra el plan de la clínica. Cuando una prueba termina o una suscripción se suspende, todas las claves responden 402 de inmediato, sin revocar nada a mano. Revocar una clave en Ajustes aplica en la siguiente petición.
Convenciones
- URL base:
https://carelycloud-production.up.railway.app - JSON de entrada y de salida. Manda Content-Type: application/json en las escrituras.
- Las fechas son ISO 8601 con zona horaria. Guardamos en UTC; manda el desfase que quieras y lo convertimos. Los huecos vuelven en la zona horaria de la clínica.
- Las listas paginadas aceptan page (desde 1) y limit. GET /appointments sin page devuelve todo el rango.
- Los errores son JSON con un código estable, un mensaje y detalles opcionales:
{ "code": "SLOT_TAKEN", "message": "SLOT_TAKEN", "details": null }Citas
| GET | /appointments | Lista citas. Filtros: start, end (ISO 8601), doctor_id, patient_id, location_id, status, q, page, limit. |
| POST | /appointments | Crea una cita. Valida horario y traslapes; la sede (location_id) la fija el servidor según el horario del profesional. |
| GET | /appointments/{id} | Una cita. |
| PATCH | /appointments/{id} | Reprograma o cambia el estado (ver transiciones). |
| DELETE | /appointments/{id} | Cancela. No se puede cancelar una cita completada ni una no asistida. |
| GET | /appointments/{id}/audit | Historial de cambios de una cita. |
Filtra por rango (start, end), profesional, paciente, estado o texto libre (q). Sin rango devuelve todo, así que manda uno.
curl "https://carelycloud-production.up.railway.app/appointments?start=2026-09-15T00:00:00Z&end=2026-09-15T23:59:59Z&doctor_id=3" \
-H "X-API-Key: cc_live_..."Crear una cita pasa por las mismas reglas que la aplicación. El profesional es obligatorio, y el hueco tiene que estar dentro de su horario y libre:
curl -X POST https://carelycloud-production.up.railway.app/appointments \
-H "X-API-Key: cc_live_..." \
-H "Content-Type: application/json" \
-d '{
"doctor_id": 3,
"service_id": 1,
"patient_id": 42,
"start_at": "2026-09-15T15:00:00Z",
"end_at": "2026-09-15T15:30:00Z",
"status": "confirmed",
"channel": "manual",
"notes": "Booked from our CRM"
}'Qué puede responder crear o reagendar, y por qué
DOCTOR_REQUIRED— no se mandó doctor_id.DOCTOR_HAS_NO_SCHEDULE— el profesional todavía no tiene horario configurado.OUTSIDE_WORKING_HOURS— la hora cae fuera de su horario, en un descanso o en un día bloqueado.SLOT_TAKEN(409) — otra cita activa se cruza.
Para no adivinar, pide primero GET /doctors/{id}/slots para una fecha y elige uno marcado available.
Los cambios de estado van por PATCH. Transiciones permitidas: scheduled → confirmed | cancelled | no_show; confirmed → completed | cancelled | no_show. Completed, cancelled y no_show son finales. DELETE es un atajo para cancelar.
curl -X PATCH https://carelycloud-production.up.railway.app/appointments/981 \
-H "X-API-Key: cc_live_..." -H "Content-Type: application/json" \
-d '{ "status": "cancelled", "cancel_reason": "Patient called" }'Pacientes
| GET | /patients | Lista pacientes. Filtros: q, doctor_id, has_no_shows, include_inactive, page, limit. |
| POST | /patients | Crea un paciente. 409 si hay un posible duplicado, salvo con force=true. |
| GET | /patients/{id} | Un paciente. |
| PATCH | /patients/{id} | Actualiza datos de contacto o notas. |
| DELETE | /patients/{id} | Archiva un paciente. Sus citas se conservan. |
| GET | /patients/{id}/appointments | Citas de un paciente, la más reciente primero. |
| GET | /patients/{id}/notes | Notas de consulta de un paciente. |
| POST | /patients/{id}/notes | Agrega una nota de consulta. |
| GET | /patients/{id}/files | Archivos adjuntos de un paciente. |
| POST | /patients/{id}/files | Adjunta un archivo (multipart/form-data, máx. 10 MB). |
| GET | /patients/export | CSV de todos los pacientes. |
| POST | /patients/import | Importa pacientes desde CSV (multipart/form-data). |
Crear un paciente busca posibles duplicados por teléfono o correo y responde 409 con las coincidencias; manda force=true para crearlo igual.
curl -X POST "https://carelycloud-production.up.railway.app/patients?force=false" \
-H "X-API-Key: cc_live_..." -H "Content-Type: application/json" \
-d '{
"first_name": "Marta",
"last_name": "Ríos",
"phone": "+50499990000",
"email": "marta@example.com",
"birth_date": "1992-04-12",
"gender": "female",
"preferred_channel": "whatsapp"
}'La búsqueda cubre nombre, teléfono y correo. Paginación de 20 por página por defecto, 500 como máximo.
curl "https://carelycloud-production.up.railway.app/patients?q=marta&page=1&limit=20" -H "X-API-Key: cc_live_..."Profesionales, disponibilidad y servicios
Solo lectura. Los profesionales y los servicios se administran desde la aplicación.
| GET | /doctors | Profesionales de la clínica. |
| GET | /doctors/{id}/availability | Horario semanal de un profesional. |
| GET | /doctors/{id}/exceptions | Días libres y cambios puntuales. |
| GET | /doctors/{id}/slots?date=YYYY-MM-DD&service_id=N | Espacios reservables de un profesional para un día. |
| GET | /doctors/{id}/services | Servicios que ofrece un profesional. |
| GET | /services | Catálogo de servicios con duración y precio. |
| GET | /locations | Sedes activas de la clínica, la principal primero. |
| GET | /clinics/me | Tu clínica: nombre, zona horaria, plan. |
| GET | /dashboard/stats | Los números de hoy, como los muestra el panel. |
curl "https://carelycloud-production.up.railway.app/doctors/3/slots?date=2026-09-15&service_id=1" -H "X-API-Key: cc_live_..."
[
{ "start_at": "2026-09-15T15:00:00-06:00", "end_at": "2026-09-15T15:30:00-06:00", "available": true },
{ "start_at": "2026-09-15T15:15:00-06:00", "end_at": "2026-09-15T15:45:00-06:00", "available": false }
]Webhooks
Un administrador registra una URL https en Ajustes → Integraciones y elige los eventos. Hacemos POST de cada uno cuando ocurre, una vez, con 8 segundos de espera. Sin reintentos: si tu endpoint está caído, la entrega queda como fallida en el registro y puedes reconstruir desde la API.
| appointment.created | se creó una cita (aplicación, página de reservas o API) |
| appointment.updated | cambió la hora, el profesional, el servicio o las notas |
| appointment.confirmed | pasó a confirmada |
| appointment.completed | pasó a completada |
| appointment.cancelled | cancelada (por el personal, el paciente o la API) |
| appointment.no_show | marcada como no-show |
| patient.created | se creó un paciente (aplicación, página de reservas, importación o API) |
Cada entrega lleva el nombre del evento y una firma HMAC-SHA256 del cuerpo, con el secreto que se mostró una vez al crear el endpoint:
POST https://your-server.example.com/carelycloud
Content-Type: application/json
X-CarelyCloud-Event: appointment.confirmed
X-CarelyCloud-Signature: sha256=3f1a...
{
"event": "appointment.confirmed",
"created_at": "2026-09-15T14:02:11.480Z",
"data": {
"id": 981, "clinic_id": 6, "doctor_id": 3, "service_id": 1, "patient_id": 42,
"start_at": "2026-09-15T15:00:00Z", "end_at": "2026-09-15T15:30:00Z",
"status": "confirmed", "channel": "online"
}
}Verifica la firma antes de confiar en el contenido. Compara con una función de tiempo constante:
# Python
import hmac, hashlib
def verify(secret: str, raw_body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
// Node
const crypto = require("crypto")
function verify(secret, rawBody, header) {
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex")
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header))
}- Solo URLs https en hosts públicos. No se siguen redirecciones.
- Responde 2xx en menos de 8 segundos; haz el trabajo después.
- El registro guarda las últimas 100 entregas por endpoint, con estado y duración.
- El botón Probar en Ajustes manda un evento de ejemplo para revisar tu endpoint antes de salir en vivo.
Zapier, n8n y Make
No necesitan una app nativa: un disparador por webhook más una acción HTTP cubren toda la API.
- Zapier: el disparador "Webhooks by Zapier · Catch Hook" recibe los eventos; la acción "Webhooks by Zapier · Custom Request" llama a la API con la cabecera X-API-Key.
- n8n: el nodo Webhook recibe los eventos; el nodo HTTP Request llama a la API. Usa una credencial Header Auth para la clave.
- Make: el módulo Custom Webhook recibe los eventos; el módulo HTTP llama a la API.
Primera automatización típica: patient.created → agregar el paciente a tu CRM; appointment.confirmed → avisar al grupo de Slack o WhatsApp de la clínica.
Errores
| 401 | INVALID_API_KEY | Clave ausente, desconocida o revocada. |
| 402 | TRIAL_EXPIRED · SUBSCRIPTION_INACTIVE · PLAN_UPGRADE_REQUIRED | El plan de la clínica no permite integraciones ahora mismo. |
| 403 | API_KEY_SCOPE | La clave no alcanza esta ruta (ver Autenticación). |
| 404 | APPOINTMENT_NOT_FOUND · PATIENT_NOT_FOUND · DOCTOR_NOT_FOUND · SERVICE_NOT_FOUND | El recurso no existe en esta clínica. |
| 409 | SLOT_TAKEN | Cita que se cruza, o paciente probablemente duplicado (manda force=true). |
| 422 | VALIDATION_ERROR · OUTSIDE_WORKING_HOURS · DOCTOR_HAS_NO_SCHEDULE · INVALID_TRANSITION | Falló la validación; details dice qué campo. |
| 429 | RATE_LIMITED | Demasiadas peticiones; espera y reintenta. |
¿Falta algo, o estás construyendo algo que necesita más que esto? Escríbenos: respondemos.