No atendemos emergencias vitales: llama al 112.

+34 611 673 280

DesarrolladoresAPI v1

Guías

Avisos

Crear avisos (todos los campos, prioridades, citas, dirección y paciente), consultarlos, listarlos con filtros y paginación, conocer la hora estimada de llegada, cancelarlos y descargar el informe asistencial.

Última revisión: 1 de octubre de 2026

Un aviso es una petición de visita de un profesional sanitario al domicilio de un paciente. Esta guía cubre todo lo que puedes hacer con ellos; los detalles de cada campo también están en la referencia de la API.

MétodoRutaÁmbitoPara qué
POST/visitsvisits:writeCrear un aviso
GET/visits/{id}visits:readConsultar un aviso
GET/visitsvisits:readListar avisos
GET/visits/{id}/etavisits:readHora estimada de llegada
POST/visits/{id}/cancelvisits:writeCancelar
GET/visits/{id}/reportreports:readInforme asistencial

Crear un aviso

POST /visits con el paciente, el domicilio, el tipo de servicio y el motivo. Envía siempre una cabecera Idempotency-Key única para esa alta: si la conexión se corta y repites la petición, no se crea un aviso duplicado (idempotencia).

Shell
curl -X POST "$SERAD_API_URL/visits" \
  -H "X-Api-Key: $SERAD_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-exp-2026-004512-1" \
  -d '{
    "patient": {
      "firstName": "María",
      "lastName": "García López",
      "documentType": "DNI",
      "documentNumber": "12345678Z",
      "birthDate": "1948-05-17",
      "sex": "FEMALE",
      "phone": "+34600111222"
    },
    "address": {
      "line1": "Calle de Alcalá 120",
      "line2": "3º B",
      "postalCode": "28009",
      "city": "Madrid",
      "province": "Madrid",
      "accessNotes": "Portal 2, código 1234#. Ascensor al fondo."
    },
    "serviceTypeCode": "GENERAL_CONSULTATION",
    "priority": "URGENT",
    "reason": "Fiebre alta desde ayer, 39 ºC, malestar general",
    "externalRef": "EXP-2026-004512",
    "callerName": "Ana García (hija)",
    "callerPhone": "+34600111333"
  }'

La respuesta es 201 Created con el aviso completo (ver la respuesta del aviso). Si repites la misma petición con la misma Idempotency-Key, recibes 200 OK con el mismo aviso y la cabecera Idempotent-Replayed: true.

Campos del alta

CampoTipoObligatorioDescripción
patientobjetoSí, salvo que envíes patientIdDatos del paciente. Ver el paciente.
patientIduuidNoPaciente de un aviso anterior tuyo. Si llega, patient no hace falta.
addressobjetoSíDomicilio de la visita. Ver la dirección.
serviceTypeCodestringSí, o serviceTypeIdCódigo del tipo de servicio, del catálogo. Recomendado: es igual en todos los entornos.
serviceTypeIduuidSí, o serviceTypeCodeId del tipo de servicio. Cambia entre entornos; si llegan los dos, manda el código.
priorityURGENT | NORMAL | SCHEDULEDNoPor defecto NORMAL, o SCHEDULED si envías scheduledFrom. Ver prioridad y citas.
scheduledFromfecha-hora ISO 8601Solo en citasInicio de la cita, con zona horaria (2026-10-02T09:00:00+02:00).
estimatedMinutesenteroNoDuración estimada: de 30 a 240, en bloques de 10. Por defecto, 60.
reasonstring (3–2000)SíMotivo de la solicitud, tal y como lo describiría el paciente o el médico que lo deriva.
externalRefstring (≤ 100)Si tu contrato lo exigeTu nº de expediente. Ver catálogo.
insurerIduuidSolo intermediariosAseguradora final, de catalog.insurers.
callerNamestring (≤ 200)NoQuién solicita el aviso, si no es el paciente («Ana García (hija)»).
callerPhonestringNoTeléfono de contacto de quien solicita. Mismo formato que patient.phone.
scheduledTofecha-horaNoObsoleto: usa estimatedMinutes. Si llega sin él, la duración de la franja se redondea al bloque de 10 minutos.

Cabeceras del alta:

CabeceraObligatoriaDescripción
Idempotency-KeyRecomendadaIdentificador único de la operación en tu sistema (≤ 200 caracteres).
X-Sandbox-SimulationNoSolo sandbox: manual para que el aviso no avance solo.

Prioridad y citas

priorityCuándo usarlaQué pasa
URGENTUrgencia no vital: el paciente necesita atención lo antes posible (fiebre alta, dolor intenso, caída leve).Se asigna con preferencia a la primera unidad compatible disponible.
NORMALHay que atenderlo hoy, sin hora concreta.Se asigna según la carga y la ruta de las unidades.
SCHEDULEDCita a una hora concreta (curas, controles, seguimiento).La unidad llega dentro de la franja de la cita.

Para una cita, envía scheduledFrom (inicio) y estimatedMinutes (cuánto durará la atención). El fin de la cita lo calculamos nosotros: scheduledTo = scheduledFrom + estimatedMinutes.

JSON
{
  "priority": "SCHEDULED",
  "scheduledFrom": "2026-10-02T09:00:00+02:00",
  "estimatedMinutes": 40
}

Reglas de la cita:

  • scheduledFrom lleva siempre zona horaria (+02:00, Z). Sin ella, el servidor la interpretaría en UTC.
  • No puede empezar más de 15 minutos en el pasado (/errors/visits/schedule-in-past): se admite el pequeño retraso entre la llamada del paciente y el alta, pero no programar citas pasadas.
  • No puede estar a más de 90 días vista (/errors/visits/schedule-beyond-horizon).
  • estimatedMinutes va de 30 a 240 en bloques de 10 (30, 40, 50… 240). Cualquier otro valor responde 400 de validación. Si no lo envías, se asumen 60 minutos.

El tiempo estimado se puede ampliar en la centralita con el aviso en curso; el valor actualizado sale en estimatedMinutes al consultar el aviso.

La dirección

CampoTipoObligatorioDescripción
line1string (1–255)SíCalle y número: Calle de Alcalá 120.
line2string (≤ 255)NoPiso, puerta, escalera: 3º B.
postalCodestring (4–10)SíCódigo postal: 28009.
citystring (1–100)SíMunicipio: Madrid.
provincestring (≤ 100)NoProvincia.
accessNotesstring (≤ 1000)NoIndicaciones para llegar: portal, código del portero, si hay que llamar antes, animales… Solo las ve la unidad.
latitude / longitudenúmeroNoCoordenadas del domicilio, si las tienes.

Si no envías coordenadas, geocodificamos la dirección. Si la geocodificación falla o el código postal no cuadra con la calle, el aviso se crea igualmente y la centralita revisa la dirección antes de asignarlo, lo que puede retrasar la salida. Para evitarlo:

  • Separa bien los campos: el número en line1, el piso en line2.
  • Envía el código postal correcto (es lo que más falla).
  • Si tu sistema ya tiene las coordenadas validadas del domicilio, envíalas.

La cobertura es Madrid y la Comunidad de Madrid. Si dudas de si una dirección está cubierta, pregúntanos.

El paciente

CampoTipoObligatorioDescripción
firstNamestring (1–100)SíNombre.
lastNamestring (1–150)SíApellidos.
documentTypeDNI | NIE | PASSPORT | NONENoPor defecto NONE (menores sin DNI, o quien no lo aporta).
documentNumberstring (≤ 30)Sí, salvo NONENº de documento. El DNI y el NIE se validan (formato y letra). Con NONE no se admite.
birthDatefecha AAAA-MM-DDRecomendadoFecha de nacimiento: no futura y de hace menos de 120 años.
sexMALE | FEMALE | OTHER | UNKNOWNNoSexo registrado (es relevante clínicamente).
phonestringRecomendadoTeléfono del paciente, con o sin prefijo: +34600111222. La unidad llama a este número si no encuentra el domicilio.

Cómo identificamos al paciente. Con el tipo y nº de documento y la fecha de nacimiento buscamos si el paciente ya existe en Salud en Ruta; si existe, el aviso se le asocia (así el profesional tiene su historia); si no, se crea. Sin documento (NONE) no hay forma segura de saber si es la misma persona, así que se crea una ficha nueva cada vez. Por eso te pedimos que envíes siempre el documento y la fecha de nacimiento cuando los tengas. Más detalles, y qué datos devolvemos, en pacientes y privacidad.

Paciente de avisos anteriores. Si un paciente ya tuvo un aviso tuyo, puedes enviar su patientId en lugar de patient. Solo admitimos pacientes de tus avisos: un patientId que no lo sea responde 404 (/errors/patients/not-found), igual que uno que no existe. Lo más sencillo por API, de todas formas, es volver a enviar los datos del paciente: el resultado es el mismo.

Errores frecuentes del alta

EstadotypeCausa
400/errors/validation-errorUn campo no cumple el formato. errors[] dice cuál (patient.documentNumber, estimatedMinutes…).
400/errors/provider-api/service-type-requiredNo has enviado ni serviceTypeCode ni serviceTypeId.
400/errors/provider/service-type-not-allowedEse tipo de servicio no está en tu catálogo.
400/errors/visits/external-ref-requiredTu contrato exige externalRef.
400/errors/provider/insurer-not-managedinsurerId no es una de tus aseguradoras (o no eres intermediario).
400/errors/patients/document-invalidEl DNI o NIE no es válido (formato o letra de control).
400/errors/visits/schedule-in-past · /errors/visits/schedule-beyond-horizonLa cita está en el pasado o a más de 90 días.
409/errors/provider-api/idempotency-key-reusedHas reutilizado una Idempotency-Key con otro cuerpo.

Todos los códigos, en errores.

La respuesta del aviso

Todas las operaciones sobre un aviso devuelven la misma estructura (dentro de data):

JSON
{
  "id": "c6a3c348-40b6-4158-a44a-7d85f01a0201",
  "code": "AV-000123",
  "status": "EN_ROUTE",
  "statusLabel": "En camino",
  "priority": "URGENT",
  "origin": "PROVIDER_API",
  "externalRef": "EXP-2026-004512",
  "serviceType": { "id": "57605fe9-bade-4040-b403-5eeb58e57c9e", "code": "GENERAL_CONSULTATION", "name": "Consulta general" },
  "requiredProfessional": "DOCTOR",
  "insurer": null,
  "patientName": "María García López",
  "address": { "line1": "Calle de Alcalá 120", "city": "Madrid", "postalCode": "28009" },
  "serviceDate": "2026-10-01",
  "scheduledFrom": null,
  "scheduledTo": null,
  "estimatedMinutes": 60,
  "milestones": {
    "requestedAt": "2026-10-01T09:10:00.000Z",
    "assignedAt": "2026-10-01T09:15:00.000Z",
    "departedAt": "2026-10-01T09:32:00.000Z",
    "arrivedAt": null,
    "finishedAt": null,
    "cancelledAt": null,
    "invoicedAt": null
  },
  "unit": { "type": "MEDICAL", "label": "Unidad médica" },
  "outcome": null,
  "cancellationReason": null,
  "cancelledWithCharge": false,
  "cancellable": false,
  "reportAvailable": false,
  "invoiceId": null,
  "createdAt": "2026-10-01T09:10:00.000Z",
  "updatedAt": "2026-10-01T09:32:00.000Z"
}
CampoDescripción
idIdentificador del aviso. Úsalo en las demás llamadas. Cambia entre entornos.
codeCódigo legible (AV-000123): el que te dirá la centralita por teléfono.
status / statusLabelEstado (ciclo de vida) y su texto en el idioma de la petición.
originPor dónde entró: PROVIDER_API, PROVIDER_PORTAL o INTRANET (por teléfono).
serviceDateDía del servicio (AAAA-MM-DD, hora de Madrid).
milestonesHora de cada hito; null si aún no ha ocurrido. Es la fuente fiable para saber qué ha pasado.
unitSolo el tipo de unidad (MEDICAL/NURSING) desde la asignación. Nunca nombres, matrículas ni posición.
outcomeResultado al finalizar: ATTENDED (atendido), PATIENT_ABSENT (no estaba), REFERRED_HOSPITAL (derivado al hospital), REFUSED (rechazó la atención) o CANCELLED.
cancellableSi puedes cancelarlo ahora mismo por la API.
cancelledWithChargeSi la cancelación tiene coste (solo las que hace la centralita con la unidad ya en camino, según tu contrato).
reportAvailableSi hay informe descargable.
invoiceIdFactura en la que está incluido, cuando se factura.

Consultar un aviso

GET /visits/{id} devuelve el aviso con su línea de tiempo (timeline): cada cambio de estado con su hora.

Shell
curl "$SERAD_API_URL/visits/c6a3c348-40b6-4158-a44a-7d85f01a0201" \
  -H "X-Api-Key: $SERAD_API_KEY"
JSON
{
  "timeline": [
    { "status": "PENDING", "label": "Aviso recibido", "at": "2026-10-01T09:10:00.000Z" },
    { "status": "ASSIGNED", "label": "Unidad asignada", "at": "2026-10-01T09:15:00.000Z" },
    { "status": "EN_ROUTE", "label": "Unidad en camino", "at": "2026-10-01T09:32:00.000Z" }
  ]
}

Un aviso de otro proveedor responde 404, igual que uno que no existe: no confirmamos que existan avisos que no son tuyos.

Listar avisos

GET /visits devuelve tus avisos, del más reciente al más antiguo, paginados.

Shell
curl "$SERAD_API_URL/visits?status=PENDING,ASSIGNED,EN_ROUTE&dateFrom=2026-10-01&page=1&limit=50" \
  -H "X-Api-Key: $SERAD_API_KEY"
ParámetroDescripción
pagePágina, desde 1. Por defecto 1.
limitAvisos por página, de 1 a 100. Por defecto 10.
statusUno o varios estados, separados por comas o repitiendo el parámetro: status=PENDING,ASSIGNED.
priorityURGENT, NORMAL o SCHEDULED.
dateFrom / dateToDía de servicio desde / hasta (incluidos), AAAA-MM-DD. No pueden ser futuros, y dateTo no puede ser anterior a dateFrom.
externalRefTu nº de expediente exacto. Es la forma de encontrar un aviso sin guardar nuestro id.
serviceTypeIdTipo de servicio.
searchTexto en el código del aviso o en el nº de expediente.
sortOrderDESC (por defecto, más recientes primero) o ASC, por hora de solicitud.
JSON
{
  "status": 200,
  "data": {
    "items": [ { "id": "c6a3c348-…", "code": "AV-000123", "status": "EN_ROUTE", "…": "…" } ],
    "pagination": { "page": 1, "limit": 50, "totalItems": 134, "totalPages": 3 }
  },
  "message": "Operación realizada correctamente"
}

Para recorrer todos, pide páginas hasta page = totalPages. Si tu objetivo es mantener tu sistema sincronizado, usa los webhooks y deja el listado para una reconciliación periódica (por ejemplo, cada noche, los avisos del día).

Hora estimada de llegada

Con la unidad en camino (EN_ROUTE), GET /visits/{id}/eta calcula cuánto le falta para llegar, por carretera, desde su última posición conocida:

Shell
curl "$SERAD_API_URL/visits/c6a3c348-40b6-4158-a44a-7d85f01a0201/eta" \
  -H "X-Api-Key: $SERAD_API_KEY"
JSON
{
  "status": 200,
  "data": {
    "status": "EN_ROUTE",
    "etaMinutes": 14,
    "estimatedArrivalAt": "2026-10-01T09:46:00.000Z",
    "estimated": false,
    "calculatedAt": "2026-10-01T09:32:10.000Z"
  },
  "message": "Operación realizada correctamente"
}
  • En cualquier otro estado, o si no hay posición reciente de la unidad, etaMinutes y estimatedArrivalAt son null.
  • estimated: true indica que no se pudo calcular la ruta por carretera y la cifra es una aproximación en línea recta: tómala con más margen.
  • Nunca devolvemos la posición de la unidad, solo el tiempo.
  • La ETA cambia con el tráfico. Si la enseñas al paciente, refréscala cada pocos minutos, no cada segundo (cuenta para tu límite de peticiones).

Cancelar un aviso

POST /visits/{id}/cancel con el motivo:

Shell
curl -X POST "$SERAD_API_URL/visits/c6a3c348-40b6-4158-a44a-7d85f01a0201/cancel" \
  -H "X-Api-Key: $SERAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "El paciente ya no necesita la visita" }'
CampoTipoObligatorioDescripción
reasonstring (3–1000)SíMotivo de la cancelación. Lo ve la centralita.

Reglas:

  • Solo se puede cancelar por la API mientras el aviso está PENDING o ASSIGNED, y entonces no tiene coste. El campo cancellable de la respuesta te dice si se puede en este momento.
  • Con la unidad en camino o en el domicilio, la API responde 409 con el tipo /errors/provider/visit-not-cancellable. En ese caso llama a la centralita (+34 611 673 280): la unidad ya ha salido y hay que avisarla. Según tu contrato, esa cancelación puede tener coste (cancelledWithCharge).
  • Un aviso ya finalizado o cancelado tampoco se puede cancelar (mismo 409).
  • Al cancelar recibes el webhook visit.cancelled.

Informe asistencial

Cuando el profesional firma el informe de la visita, puedes descargarlo en PDF con GET /visits/{id}/report (ámbito reports:read), solo si:

  1. el informe está firmado,
  2. Salud en Ruta lo ha compartido con tu organización, y
  3. el paciente ha consentido que se te envíe.
Shell
curl "$SERAD_API_URL/visits/c6a3c348-40b6-4158-a44a-7d85f01a0201/report" \
  -H "X-Api-Key: $SERAD_API_KEY" \
  -o informe-AV-000123.pdf
  • La respuesta es el PDF (Content-Type: application/pdf, Content-Disposition: attachment; filename="informe-AV-000123.pdf"), no JSON.
  • Si no se cumple alguna de las tres condiciones, responde 404 con el tipo /errors/provider/report-not-available. Sin consentimiento del paciente no hay informe, y no podemos saltarnos esa regla.
  • Sabrás que está disponible por el webhook report.shared o por reportAvailable: true al consultar el aviso.
  • Cada descarga queda registrada como acceso a datos clínicos, con la clave que la hizo. Descárgalo una vez, guárdalo en tu sistema documental con las medidas que exige el RGPD para datos de salud, y no lo vuelvas a pedir en cada consulta.

Más sobre los datos de salud en pacientes y privacidad.

¿Falta algo o algo no cuadra con lo que ves? Escribe al equipo técnico: info@saludenruta.es.