{"openapi":"3.1.0","info":{"title":"Train Radar API — TROP v1","version":"0.1.0","description":"Train Radar Open Protocol: ingesta abierta de observaciones de posicion de trenes, estado fusionado y alertas del motor de seguridad. Gratuito, sin cuentas; una clave de fuente (X-Source-Key) es opcional y solo sube la confianza. Complemento informativo de la senalizacion oficial.","license":{"name":"PolyForm-Noncommercial-1.0.0 (uso comercial: licencia de pago)","url":"https://github.com/jorgealferez/train_radar/blob/main/COMMERCIAL-LICENSE.md"}},"servers":[{"url":"/"}],"tags":[{"name":"ingesta","description":"Observaciones de posicion (moviles, operadores, sistemas externos)"},{"name":"trenes","description":"Estado fusionado de los trenes"},{"name":"seguridad","description":"Alertas del motor de seguridad"},{"name":"estaciones","description":"Estaciones y tableros de salidas y llegadas"},{"name":"sistema","description":"Salud, fuentes y documentacion"},{"name":"historico","description":"Historico guardado en base de datos"},{"name":"avisos","description":"Seguir un tren con notificaciones Web Push, sin cuenta (ADR 0021)"},{"name":"gtfs-rt","description":"Salida abierta para la red: feeds GTFS-Realtime con lo que el radar fusiona (ADR 0027)"}],"paths":{"/health":{"get":{"tags":["sistema"],"summary":"Salud del servicio","operationId":"getHealth","responses":{"200":{"description":"Servicio operativo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/api/v1/observations":{"post":{"tags":["ingesta"],"summary":"Ingesta de un lote de observaciones (hasta 100)","operationId":"postObservations","description":"Cada observacion se valida por separado: las validas se aceptan y las invalidas se listan en `errors`. Solo entran fuentes verificables (ADR 0012): un sistema externo con la cabecera X-Source-Key (pasa a `operator`) o un conductor con la cabecera X-Driver-Key (observaciones `phone` con el tren que conduce). Sin clave valida, 401. Los `source.kind` internos (digitraffic, gtfs-rt, simulator) estan reservados.","parameters":[{"name":"X-Source-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Clave de fuente (TR_SOURCE_KEYS) de un sistema externo. Sube la confianza a operator."},{"name":"X-Driver-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Clave de conductor (TR_DRIVER_KEYS). Autoriza observaciones phone del tren que conduce; el conductor se publica como fuente \"driver\" sin identificarlo."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObservationBatch"}}}},"responses":{"202":{"description":"Lote procesado (puede incluir rechazos individuales)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestResult"}}}},"400":{"description":"El cuerpo no es un lote valido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Clave de fuente o de conductor desconocida, o lote sin ninguna clave","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Limite de peticiones por IP superado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/trains":{"get":{"tags":["trenes"],"summary":"Trenes con estado fusionado","operationId":"listTrains","parameters":[{"name":"bbox","in":"query","required":false,"schema":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?(,-?\\d+(\\.\\d+)?){3}$"},"description":"minLon,minLat,maxLon,maxLat"},{"name":"maxAgeS","in":"query","required":false,"schema":{"type":"number","minimum":0},"description":"Solo trenes observados hace menos de estos segundos"}],"responses":{"200":{"description":"Lista de trenes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainList"}}}},"400":{"description":"Filtro invalido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/trains/{key}":{"get":{"tags":["trenes"],"summary":"Estado de un tren por clave","operationId":"getTrain","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tren","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainState"}}}},"404":{"description":"Tren desconocido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/trains/{key}/timetable":{"get":{"tags":["trenes"],"summary":"Horario del tren: paradas con hora prevista, estimada y real, y retraso","operationId":"getTrainTimetable","description":"Cruza el horario publicado por la fuente (Digitraffic, GTFS-RT TripUpdates) con la posicion fusionada: `summary.delayS` es el retraso del operador y `summary.positionDelayS` el estimado por Train Radar desde la posicion.","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Horario","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainTimetable"}}}},"404":{"description":"Tren desconocido o sin horario","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/stations":{"get":{"tags":["estaciones"],"summary":"Estaciones conocidas por los horarios cargados y las fuentes","operationId":"listStations","responses":{"200":{"description":"Estaciones con coordenadas y trenes proximos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationList"}}}}}}},"/api/v1/stations/{key}/board":{"get":{"tags":["estaciones"],"summary":"Tablero de salidas y llegadas de una estacion (30 min atras, 3 h adelante)","operationId":"getStationBoard","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Salidas y llegadas con prevista, estimada, real, via y retraso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationBoard"}}}},"404":{"description":"Estacion desconocida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/alerts":{"get":{"tags":["seguridad"],"summary":"Alertas activas y recientes","operationId":"listAlerts","responses":{"200":{"description":"Alertas","content":{"application/json":{"schema":{"type":"object","properties":{"alerts":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}}},"required":["alerts"]}}}}}}},"/api/v1/history/status":{"get":{"tags":["historico"],"summary":"Estado del historico en base de datos (conexion, retencion, filas escritas)","operationId":"getHistoryStatus","responses":{"200":{"description":"Estado","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/trains/{key}/track":{"get":{"tags":["historico"],"summary":"Traza guardada de un tren (puntos muestreados: 1 km o 60 s en marcha, 10 min parado)","operationId":"getTrainTrack","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"hours","in":"query","required":false,"schema":{"type":"number","minimum":1,"maximum":168}}],"responses":{"200":{"description":"Traza","content":{"application/json":{"schema":{"type":"object"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/trains/{key}/history":{"get":{"tags":["historico"],"summary":"Circulaciones pasadas del tren con su paso por cada parada: prevista, real, estimada, retraso y tiempo en la estacion","operationId":"getTrainHistory","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","required":false,"schema":{"type":"number","minimum":1,"maximum":60}}],"responses":{"200":{"description":"Historico","content":{"application/json":{"schema":{"type":"object"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/stations/{key}/history":{"get":{"tags":["historico"],"summary":"Historico de una estacion: puntualidad (a tiempo <= 5 min), reparto por hora y por dia, tiempo medio en la estacion y ultimos pasos","operationId":"getStationHistory","description":"Agrega los ids de parada de la misma estacion en todas las fuentes (a menos de 300 m). El retraso de cada paso es hora real menos prevista o, sin hora real, el ultimo retraso publicado en esa parada (`delayBase`). Informativo, no oficial (ADR 0029).","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","required":false,"schema":{"type":"number","minimum":1,"maximum":60}}],"responses":{"200":{"description":"Historico de la estacion","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationHistory"}}}},"404":{"description":"Estacion desconocida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/trains/{key}/certificate":{"get":{"tags":["historico"],"summary":"Certificado de retraso de una circulacion guardada (JSON con hash y firma opcional)","operationId":"getDelayCertificate","description":"Lo que el radar observo de la circulacion del dia indicado (o la ultima): horario previsto, real y estimado por parada, retraso en destino con su base (real, estimada o ultimo conocido), fuentes y ventana de observacion. `id` es el SHA-256 del contenido; con TR_CERT_SECRET lleva firma HMAC verificable en /api/v1/certificates/verify. Documento informativo de apoyo a una reclamacion, no un justificante del operador (ADR 0024).","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"date","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"description":"Dia de servicio AAAA-MM-DD; sin el, la ultima circulacion vista"}],"responses":{"200":{"description":"Certificado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DelayCertificate"}}}},"400":{"description":"Fecha invalida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Sin circulacion guardada de ese tren y dia","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/certificates/verify":{"post":{"tags":["historico"],"summary":"Verificar un certificado de retraso: integridad del contenido y firma del servidor","operationId":"verifyDelayCertificate","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DelayCertificate"}}}},"responses":{"200":{"description":"Resultado de la verificacion","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateVerification"}}}},"400":{"description":"El cuerpo no es un certificado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/alerts/stats":{"get":{"tags":["seguridad"],"summary":"Estadisticas de las alertas guardadas: criticas por hora, tipos, dias, redes, vida y resolucion","operationId":"getAlertStats","description":"Calibracion continua del motor (ADR 0033) sobre el historico de alertas: reparto por tipo y severidad con vida mediana, por dia y por red de los trenes implicados, tasa de criticas por hora, % resueltas y las 10 ultimas criticas con su evidencia. Sustituye al muestreo manual de una hora.","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[1,7,30],"default":1}}],"responses":{"200":{"description":"Estadisticas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertStats"}}}},"400":{"description":"Parametro invalido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/alerts/history":{"get":{"tags":["seguridad"],"summary":"Alertas guardadas, de la mas reciente a la mas antigua, con filtros por tipo y severidad","operationId":"getAlertHistory","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[1,7,30],"default":1}},{"name":"kind","in":"query","required":false,"schema":{"type":"string"}},{"name":"severity","in":"query","required":false,"schema":{"type":"string","enum":["critical","warning","info"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Alertas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertHistory"}}}},"400":{"description":"Parametro invalido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/network/daily":{"get":{"tags":["historico"],"summary":"Evolucion diaria de la puntualidad de la red: circulaciones a tiempo (<= 5 min), leves (5-15), moderadas (15-30), severas (> 30) y canceladas, y los mayores retrasos de hoy","operationId":"getNetworkDaily","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"number","enum":[7,30]}}],"responses":{"200":{"description":"Evolucion diaria","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NetworkDaily"}}}},"400":{"description":"days distinto de 7 o 30","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/corridors":{"get":{"tags":["estado"],"summary":"Corredores (Madrid - Andalucia, Madrid - Cataluna...): trenes en vivo por sentido, puntualidad ahora y de los ultimos 7 dias frente a los 7 anteriores","operationId":"getCorridors","description":"Un tren pertenece al corredor si su origen esta en un extremo y su destino en el otro. Sin base de datos, `week` y `previousWeek` son null (ADR 0032).","responses":{"200":{"description":"Corredores","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CorridorList"}}}}}}},"/api/v1/punctuality":{"get":{"tags":["historico"],"summary":"Puntualidad historica agregada (7 o 30 dias) por operador, tipo de tren, linea, tren o estacion","operationId":"getPunctuality","description":"Calculada al pedirla sobre las circulaciones y pasos por parada guardados: % a tiempo (retraso en destino <= 5 min), retraso medio y p90, cancelaciones, maximo y peor circulacion de cada grupo. El retraso en destino es la hora real menos la prevista de la ultima parada comercial; si no hay hora real, la estimada o el ultimo retraso publicado (ADR 0025).","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,30],"default":7}},{"name":"by","in":"query","required":false,"schema":{"type":"string","enum":["operator","category","line","train","station"],"default":"operator"}}],"responses":{"200":{"description":"Informe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PunctualityReport"}}}},"400":{"description":"Parametro invalido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/trains/{key}/punctuality":{"get":{"tags":["historico"],"summary":"Puntualidad de un tren en 7 o 30 dias con sus ultimas circulaciones","operationId":"getTrainPunctuality","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,30],"default":7}}],"responses":{"200":{"description":"Puntualidad del tren","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainPunctuality"}}}},"400":{"description":"Parametro invalido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Sin circulaciones guardadas en la ventana","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sin base de datos de historico","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/notices":{"get":{"tags":["trenes"],"summary":"Avisos de servicio vigentes de los operadores (obras, cancelaciones, incidencias)","operationId":"listNotices","responses":{"200":{"description":"Avisos","content":{"application/json":{"schema":{"type":"object","properties":{"notices":{"type":"array","items":{"$ref":"#/components/schemas/ServiceNotice"}}},"required":["notices"]}}}}}}},"/api/v1/sources":{"get":{"tags":["sistema"],"summary":"Estado de las fuentes conectadas","operationId":"listSources","responses":{"200":{"description":"Fuentes","content":{"application/json":{"schema":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/components/schemas/SourceStatus"}}},"required":["sources"]}}}}}}},"/api/v1/push/config":{"get":{"tags":["avisos"],"summary":"Disponibilidad de las notificaciones push y clave publica VAPID","operationId":"getPushConfig","responses":{"200":{"description":"enabled=false sin claves VAPID o sin base de datos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushConfig"}}}}}}},"/api/v1/push/subscriptions":{"post":{"tags":["avisos"],"summary":"Seguir un tren: alta (o cambio de tren) de una suscripcion Web Push","operationId":"subscribePush","description":"Sin cuenta: la suscripcion del navegador es la identidad. Cada 30 s el servidor compara el tren con lo ultimo avisado y notifica retraso que alcanza el umbral o empeora 3 min, cambio de via de la proxima parada, llegada en pocos minutos y fin del viaje. La misma suscripcion solo sigue un tren a la vez; caduca a las 24 h y se borra si el servicio de push la da por desaparecida.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSubscriptionRequest"}}}},"responses":{"202":{"description":"Suscripcion guardada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSubscriptionResult"}}}},"400":{"description":"Cuerpo invalido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Notificaciones no disponibles en este despliegue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["avisos"],"summary":"Dejar de seguir: baja de una suscripcion por su endpoint","operationId":"unsubscribePush","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushUnsubscribe"}}}},"responses":{"204":{"description":"Baja hecha"},"400":{"description":"Cuerpo invalido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Suscripcion desconocida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Notificaciones no disponibles en este despliegue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/gtfs-rt/vehicle-positions.pb":{"get":{"tags":["gtfs-rt"],"summary":"Feed GTFS-Realtime VehiclePositions con las posiciones fusionadas de todos los trenes","operationId":"getGtfsRtVehiclePositions","description":"FeedMessage protobuf (gtfs_realtime_version 2.0, FULL_DATASET). Una entidad por tren: id y trip.trip_id son la clave del tren de la API JSON (`/api/v1/trains/{key}`), vehicle.label el tipo y numero, vehicle.license_plate el operador, trip.route_id la linea o el tipo, position con latitud, longitud, rumbo y velocidad en m/s, timestamp el instante de la ultima observacion fusionada. Cache publica de 10 s; 120 peticiones por minuto e IP. La confianza y las fuentes de cada tren, que GTFS-RT no modela, estan en la API JSON.","responses":{"200":{"description":"FeedMessage","content":{"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"429":{"description":"Limite de peticiones por IP superado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/gtfs-rt/alerts.pb":{"get":{"tags":["gtfs-rt"],"summary":"Feed GTFS-Realtime Alerts con las alertas del motor de seguridad y los avisos de servicio","operationId":"getGtfsRtAlerts","description":"FeedMessage protobuf (FULL_DATASET). Alertas del motor: una entidad por alerta con los trenes implicados como informed_entity.trip.trip_id, severity_level SEVERE/WARNING/INFO segun la severidad, effect OTHER_EFFECT, header con el tipo, description con el mensaje y url al tren en el mapa; las resueltas llevan active_period.end. Avisos de servicio de los operadores: entidad `notice:<id>` con sus route_id, effect MODIFIED_SERVICE y el texto publicado. Cache publica de 10 s; 120 peticiones por minuto e IP.","responses":{"200":{"description":"FeedMessage","content":{"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"429":{"description":"Limite de peticiones por IP superado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/openapi.json":{"get":{"tags":["sistema"],"summary":"Este documento","operationId":"getOpenApi","responses":{"200":{"description":"Especificacion OpenAPI 3.1","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"x-websocket":{"path":"/ws","description":"Mensajes JSON. Servidor -> cliente: hello, snapshot, trains (upsert/remove), alert, ack, error. Cliente -> servidor: auth (clave de conductor; respuesta auth ok/ko), observations (lote, solo aceptado tras auth o con clave de fuente), ping. Esquemas en @train-radar/protocol (messages.ts)."},"components":{"schemas":{"Observation":{"type":"object","properties":{"v":{"type":"number","const":1,"description":"Version del protocolo"},"id":{"type":"string","minLength":1,"maxLength":64,"description":"Identificador unico de la observacion en el emisor"},"source":{"type":"object","properties":{"kind":{"type":"string","enum":["phone","gtfs-rt","digitraffic","operator","simulator","other","webhook"],"description":"Tipo de fuente que emite la observacion"},"id":{"type":"string","minLength":1,"maxLength":64,"description":"Identificador estable de la fuente, p. ej. \"renfe-gtfsrt\""},"name":{"description":"Nombre legible de la fuente","type":"string","maxLength":120}},"required":["kind","id"],"additionalProperties":false,"description":"Fuente de una observacion"},"train":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":120,"description":"Clave estable del tren dentro de la fuente: \"<kind>:<id>\", p. ej. \"digitraffic:73@2026-10-29\""},"number":{"description":"Numero comercial o de circulacion","type":"string","maxLength":32},"operator":{"description":"Operador: renfe, iryo, ouigo, vr...","type":"string","maxLength":64},"category":{"description":"Categoria: AVE, Alvia, Cercanias, IC, mercancias...","type":"string","maxLength":64},"series":{"description":"Serie del material rodante (p. ej. \"449\" o \"103\"); varias unidades separadas por comas","type":"string","maxLength":32},"unknown":{"description":"true si el emisor no sabe en que tren va (movil sin tren seleccionado)","type":"boolean"}},"required":["key"],"additionalProperties":false,"description":"Como identifica el emisor al tren"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Instante de la medida en el emisor (ISO 8601)"},"position":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180},"accuracyM":{"description":"Radio de error horizontal en metros","type":"number","minimum":0,"maximum":100000},"altitudeM":{"type":"number","minimum":-500,"maximum":10000}},"required":["lat","lon"],"additionalProperties":false,"description":"Posicion WGS84"},"motion":{"type":"object","properties":{"speedKmh":{"type":"number","minimum":0,"maximum":600},"headingDeg":{"description":"Rumbo geografico, 0 = norte, sentido horario","type":"number","minimum":0,"maximum":360},"speedAccuracyKmh":{"type":"number","minimum":0,"maximum":600}},"additionalProperties":false,"description":"Movimiento medido por el emisor"},"reporter":{"type":"object","properties":{"role":{"type":"string","enum":["passenger","crew","driver","observer"],"description":"Rol declarado por quien comparte desde un movil; solo cambia la presentacion"},"deviceId":{"type":"string","minLength":8,"maxLength":64,"description":"Identificador aleatorio generado en el dispositivo; rota cada 24 h; nunca se publica"}},"required":["role","deviceId"],"additionalProperties":false,"description":"Quien comparte desde un movil"},"extra":{"description":"Campos libres del sistema origen (modo ETCS, senal de cabina, maxspeedKmh...)","type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"anyOf":[{"type":"string","maxLength":256},{"type":"number"},{"type":"boolean"}]}}},"required":["v","id","source","train","at","position"],"additionalProperties":false,"description":"Una observacion de posicion de un tren (TROP v1)"},"ObservationBatch":{"type":"object","properties":{"observations":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"v":{"type":"number","const":1,"description":"Version del protocolo"},"id":{"type":"string","minLength":1,"maxLength":64,"description":"Identificador unico de la observacion en el emisor"},"source":{"type":"object","properties":{"kind":{"type":"string","enum":["phone","gtfs-rt","digitraffic","operator","simulator","other","webhook"],"description":"Tipo de fuente que emite la observacion"},"id":{"type":"string","minLength":1,"maxLength":64,"description":"Identificador estable de la fuente, p. ej. \"renfe-gtfsrt\""},"name":{"description":"Nombre legible de la fuente","type":"string","maxLength":120}},"required":["kind","id"],"additionalProperties":false,"description":"Fuente de una observacion"},"train":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":120,"description":"Clave estable del tren dentro de la fuente: \"<kind>:<id>\", p. ej. \"digitraffic:73@2026-10-29\""},"number":{"description":"Numero comercial o de circulacion","type":"string","maxLength":32},"operator":{"description":"Operador: renfe, iryo, ouigo, vr...","type":"string","maxLength":64},"category":{"description":"Categoria: AVE, Alvia, Cercanias, IC, mercancias...","type":"string","maxLength":64},"series":{"description":"Serie del material rodante (p. ej. \"449\" o \"103\"); varias unidades separadas por comas","type":"string","maxLength":32},"unknown":{"description":"true si el emisor no sabe en que tren va (movil sin tren seleccionado)","type":"boolean"}},"required":["key"],"additionalProperties":false,"description":"Como identifica el emisor al tren"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Instante de la medida en el emisor (ISO 8601)"},"position":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180},"accuracyM":{"description":"Radio de error horizontal en metros","type":"number","minimum":0,"maximum":100000},"altitudeM":{"type":"number","minimum":-500,"maximum":10000}},"required":["lat","lon"],"additionalProperties":false,"description":"Posicion WGS84"},"motion":{"type":"object","properties":{"speedKmh":{"type":"number","minimum":0,"maximum":600},"headingDeg":{"description":"Rumbo geografico, 0 = norte, sentido horario","type":"number","minimum":0,"maximum":360},"speedAccuracyKmh":{"type":"number","minimum":0,"maximum":600}},"additionalProperties":false,"description":"Movimiento medido por el emisor"},"reporter":{"type":"object","properties":{"role":{"type":"string","enum":["passenger","crew","driver","observer"],"description":"Rol declarado por quien comparte desde un movil; solo cambia la presentacion"},"deviceId":{"type":"string","minLength":8,"maxLength":64,"description":"Identificador aleatorio generado en el dispositivo; rota cada 24 h; nunca se publica"}},"required":["role","deviceId"],"additionalProperties":false,"description":"Quien comparte desde un movil"},"extra":{"description":"Campos libres del sistema origen (modo ETCS, senal de cabina, maxspeedKmh...)","type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"anyOf":[{"type":"string","maxLength":256},{"type":"number"},{"type":"boolean"}]}}},"required":["v","id","source","train","at","position"],"additionalProperties":false,"description":"Una observacion de posicion de un tren (TROP v1)"}}},"required":["observations"],"additionalProperties":false,"description":"Lote de observaciones"},"IngestResult":{"type":"object","properties":{"accepted":{"type":"integer","minimum":0,"maximum":9007199254740991},"rejected":{"type":"integer","minimum":0,"maximum":9007199254740991},"errors":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"message":{"type":"string"}},"required":["index","message"],"additionalProperties":false}}},"required":["accepted","rejected","errors"],"additionalProperties":false,"description":"Resultado de la ingesta de un lote"},"TrainState":{"type":"object","properties":{"key":{"type":"string"},"number":{"type":"string"},"operator":{"type":"string"},"category":{"type":"string"},"series":{"description":"Serie del material rodante, si la fuente la publica","type":"string"},"unknown":{"type":"boolean"},"position":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"}},"required":["lat","lon"],"additionalProperties":false},"accuracyM":{"type":["number","null"]},"speedKmh":{"type":["number","null"]},"speedDerived":{"description":"true si la velocidad no la publica la fuente y se deriva de posiciones sucesivas (aproximada; el motor de seguridad no la usa)","type":"boolean"},"headingDeg":{"type":["number","null"]},"confidence":{"type":"number","minimum":0,"maximum":1,"description":"0..1: frescura, numero de fuentes y precision"},"lastSeenAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"ageS":{"type":"number","minimum":0,"description":"Segundos desde la ultima observacion, calculado al servir"},"sources":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["phone","gtfs-rt","digitraffic","operator","simulator","other","webhook"],"description":"Tipo de fuente que emite la observacion"},"id":{"type":"string"},"lastAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["kind","id","lastAt","count"],"additionalProperties":false}},"reporters":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moviles a bordo que comparten posicion"},"alerts":{"type":"array","items":{"type":"string"},"description":"Ids de alertas activas que implican a este tren"},"place":{"description":"Division administrativa en la que esta el tren (limites abiertos simplificados)","type":"object","properties":{"country":{"type":"string"},"region":{"description":"Comunidad autonoma o region","type":"string"},"province":{"type":"string"}},"required":["country"],"additionalProperties":false},"schedule":{"description":"Horario y puntualidad, si alguna fuente publica el horario de este tren","type":"object","properties":{"source":{"type":"string","description":"Fuente del horario (digitraffic, gtfs-rt:renfe...)"},"line":{"description":"Linea comercial (C1, R3...)","type":"string"},"origin":{"type":"string"},"destination":{"type":"string"},"scheduledDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"delayS":{"description":"Retraso publicado por el operador, en segundos","type":["number","null"]},"positionDelayS":{"description":"Retraso estimado por Train Radar cruzando la posicion con el horario, en segundos","type":["number","null"]},"punctuality":{"type":"string","enum":["on-time","minor","delayed","severe","cancelled","unknown"],"description":"on-time <= 2 min, minor <= 5 min, delayed <= 15 min, severe > 15 min"},"nextStop":{"type":"object","properties":{"name":{"type":"string"},"lat":{"description":"Coordenadas de la parada: el cliente no extrapola mas alla","type":"number"},"lon":{"type":"number"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"track":{"type":"string"}},"required":["name"],"additionalProperties":false},"hasStops":{"type":"boolean","description":"Hay tabla de paradas en /api/v1/trains/{key}/timetable"}},"required":["source","delayS","positionDelayS","punctuality","hasStops"],"additionalProperties":false}},"required":["key","position","accuracyM","speedKmh","headingDeg","confidence","lastSeenAt","ageS","sources","reporters","alerts"],"additionalProperties":false,"description":"Estado fusionado de un tren"},"TrainList":{"type":"object","properties":{"serverTime":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"trains":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"number":{"type":"string"},"operator":{"type":"string"},"category":{"type":"string"},"series":{"description":"Serie del material rodante, si la fuente la publica","type":"string"},"unknown":{"type":"boolean"},"position":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"}},"required":["lat","lon"],"additionalProperties":false},"accuracyM":{"type":["number","null"]},"speedKmh":{"type":["number","null"]},"speedDerived":{"description":"true si la velocidad no la publica la fuente y se deriva de posiciones sucesivas (aproximada; el motor de seguridad no la usa)","type":"boolean"},"headingDeg":{"type":["number","null"]},"confidence":{"type":"number","minimum":0,"maximum":1,"description":"0..1: frescura, numero de fuentes y precision"},"lastSeenAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"ageS":{"type":"number","minimum":0,"description":"Segundos desde la ultima observacion, calculado al servir"},"sources":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["phone","gtfs-rt","digitraffic","operator","simulator","other","webhook"],"description":"Tipo de fuente que emite la observacion"},"id":{"type":"string"},"lastAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["kind","id","lastAt","count"],"additionalProperties":false}},"reporters":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moviles a bordo que comparten posicion"},"alerts":{"type":"array","items":{"type":"string"},"description":"Ids de alertas activas que implican a este tren"},"place":{"description":"Division administrativa en la que esta el tren (limites abiertos simplificados)","type":"object","properties":{"country":{"type":"string"},"region":{"description":"Comunidad autonoma o region","type":"string"},"province":{"type":"string"}},"required":["country"],"additionalProperties":false},"schedule":{"description":"Horario y puntualidad, si alguna fuente publica el horario de este tren","type":"object","properties":{"source":{"type":"string","description":"Fuente del horario (digitraffic, gtfs-rt:renfe...)"},"line":{"description":"Linea comercial (C1, R3...)","type":"string"},"origin":{"type":"string"},"destination":{"type":"string"},"scheduledDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"delayS":{"description":"Retraso publicado por el operador, en segundos","type":["number","null"]},"positionDelayS":{"description":"Retraso estimado por Train Radar cruzando la posicion con el horario, en segundos","type":["number","null"]},"punctuality":{"type":"string","enum":["on-time","minor","delayed","severe","cancelled","unknown"],"description":"on-time <= 2 min, minor <= 5 min, delayed <= 15 min, severe > 15 min"},"nextStop":{"type":"object","properties":{"name":{"type":"string"},"lat":{"description":"Coordenadas de la parada: el cliente no extrapola mas alla","type":"number"},"lon":{"type":"number"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"track":{"type":"string"}},"required":["name"],"additionalProperties":false},"hasStops":{"type":"boolean","description":"Hay tabla de paradas en /api/v1/trains/{key}/timetable"}},"required":["source","delayS","positionDelayS","punctuality","hasStops"],"additionalProperties":false}},"required":["key","position","accuracyM","speedKmh","headingDeg","confidence","lastSeenAt","ageS","sources","reporters","alerts"],"additionalProperties":false,"description":"Estado fusionado de un tren"}}},"required":["serverTime","count","trains"],"additionalProperties":false},"TrainTimetable":{"type":"object","properties":{"key":{"type":"string"},"summary":{"type":"object","properties":{"source":{"type":"string","description":"Fuente del horario (digitraffic, gtfs-rt:renfe...)"},"line":{"description":"Linea comercial (C1, R3...)","type":"string"},"origin":{"type":"string"},"destination":{"type":"string"},"scheduledDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"delayS":{"description":"Retraso publicado por el operador, en segundos","type":["number","null"]},"positionDelayS":{"description":"Retraso estimado por Train Radar cruzando la posicion con el horario, en segundos","type":["number","null"]},"punctuality":{"type":"string","enum":["on-time","minor","delayed","severe","cancelled","unknown"],"description":"on-time <= 2 min, minor <= 5 min, delayed <= 15 min, severe > 15 min"},"nextStop":{"type":"object","properties":{"name":{"type":"string"},"lat":{"description":"Coordenadas de la parada: el cliente no extrapola mas alla","type":"number"},"lon":{"type":"number"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"track":{"type":"string"}},"required":["name"],"additionalProperties":false},"hasStops":{"type":"boolean","description":"Hay tabla de paradas en /api/v1/trains/{key}/timetable"}},"required":["source","delayS","positionDelayS","punctuality","hasStops"],"additionalProperties":false,"description":"Resumen del horario de un tren cruzado con su posicion"},"stops":{"type":"array","items":{"type":"object","properties":{"stationId":{"type":"string"},"name":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"commercial":{"type":"boolean","description":"Parada con viajeros (false = paso o parada tecnica)"},"track":{"description":"Via o anden comercial","type":"string"},"cancelled":{"type":"boolean"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduledDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"actualArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"actualDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"delayS":{"description":"Retraso en la parada (real o estimado por el operador), en segundos","type":["number","null"]},"passed":{"type":"boolean","description":"El tren ya ha pasado por la parada"}},"required":["stationId","name","commercial","cancelled","delayS","passed"],"additionalProperties":false,"description":"Paso previsto, estimado y real de un tren por una parada"}},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["key","summary","stops","updatedAt"],"additionalProperties":false,"description":"Horario completo de un tren con tiempos previstos, estimados y reales"},"Alert":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["SUDDEN_STOP","STOPPED_ON_LINE","CONVERGENCE","OVERSPEED","SIGNAL_LOST"]},"severity":{"type":"string","enum":["info","warning","critical"]},"trains":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Claves de los trenes implicados"},"position":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"}},"required":["lat","lon"],"additionalProperties":false},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"resolvedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"evidence":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["number","string"]}},"message":{"type":"string"}},"required":["id","kind","severity","trains","position","createdAt","updatedAt","evidence","message"],"additionalProperties":false,"description":"Alerta del motor de seguridad: informativa, con su evidencia"},"SourceStatus":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["phone","gtfs-rt","digitraffic","operator","simulator","other","webhook"],"description":"Tipo de fuente que emite la observacion"},"name":{"type":"string"},"enabled":{"type":"boolean"},"healthy":{"type":"boolean"},"lastSuccessAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"lastError":{"type":["string","null"]},"observations":{"type":"integer","minimum":0,"maximum":9007199254740991},"rejected":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Observaciones rechazadas por el nucleo (antiguas, invalidas)"},"trains":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["id","kind","name","enabled","healthy","lastSuccessAt","lastError","observations","rejected","trains"],"additionalProperties":false,"description":"Estado de una fuente conectada"},"StationList":{"type":"object","properties":{"serverTime":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"stations":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Clave estable: \"<fuente>:<id>\", p. ej. \"gtfs-rt:renfe:17000\""},"id":{"type":"string"},"source":{"type":"string","description":"Fuente que la publica (digitraffic, gtfs-rt:renfe, gtfs-rt:renfe-ld...)"},"name":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"upcoming":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["key","id","source","name","lat","lon","upcoming"],"additionalProperties":false,"description":"Estacion conocida por los horarios cargados o por las fuentes"}}},"required":["serverTime","count","stations"],"additionalProperties":false},"StationBoard":{"type":"object","properties":{"station":{"type":"object","properties":{"key":{"type":"string","description":"Clave estable: \"<fuente>:<id>\", p. ej. \"gtfs-rt:renfe:17000\""},"id":{"type":"string"},"source":{"type":"string","description":"Fuente que la publica (digitraffic, gtfs-rt:renfe, gtfs-rt:renfe-ld...)"},"name":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"upcoming":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["key","id","source","name","lat","lon","upcoming"],"additionalProperties":false,"description":"Estacion conocida por los horarios cargados o por las fuentes"},"serverTime":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"from":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"to":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"departures":{"type":"array","items":{"type":"object","properties":{"trainKey":{"type":"string"},"number":{"type":"string"},"line":{"type":"string"},"origin":{"type":"string"},"destination":{"type":"string"},"kind":{"type":"string","enum":["departure","arrival","pass"],"description":"Salida (primera parada o con salida), llegada (ultima parada) o paso sin parada"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduledDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"actualArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"actualDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"delayS":{"type":["number","null"]},"track":{"type":"string"},"cancelled":{"type":"boolean"},"passed":{"type":"boolean"},"live":{"type":"boolean"},"realtime":{"description":"false: solo horario planificado (GTFS estatico), sin dato en tiempo real del viaje","type":"boolean"}},"required":["trainKey","kind","delayS","cancelled","passed","live"],"additionalProperties":false,"description":"Una fila del tablero de una estacion"}},"arrivals":{"type":"array","items":{"type":"object","properties":{"trainKey":{"type":"string"},"number":{"type":"string"},"line":{"type":"string"},"origin":{"type":"string"},"destination":{"type":"string"},"kind":{"type":"string","enum":["departure","arrival","pass"],"description":"Salida (primera parada o con salida), llegada (ultima parada) o paso sin parada"},"scheduledArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"scheduledDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"estimatedDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"actualArrival":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"actualDeparture":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"delayS":{"type":["number","null"]},"track":{"type":"string"},"cancelled":{"type":"boolean"},"passed":{"type":"boolean"},"live":{"type":"boolean"},"realtime":{"description":"false: solo horario planificado (GTFS estatico), sin dato en tiempo real del viaje","type":"boolean"}},"required":["trainKey","kind","delayS","cancelled","passed","live"],"additionalProperties":false,"description":"Una fila del tablero de una estacion"}},"atStation":{"description":"Trenes en vivo parados ahora en la estacion","type":"array","items":{"type":"object","properties":{"trainKey":{"type":"string"},"number":{"type":"string"},"category":{"type":"string"},"line":{"type":"string"},"origin":{"type":"string"},"destination":{"type":"string"},"delayS":{"type":["number","null"]},"distanceM":{"type":"number","minimum":0,"description":"Distancia de la ultima posicion a la estacion"},"ageS":{"type":"number","minimum":0}},"required":["trainKey","delayS","distanceM","ageS"],"additionalProperties":false,"description":"Tren parado ahora mismo en la estacion (velocidad <= 5 km/h a menos de 300 m)"}}},"required":["station","serverTime","from","to","departures","arrivals"],"additionalProperties":false,"description":"Tablero de salidas y llegadas de una estacion, calculado sobre los horarios en memoria"},"ServiceNotice":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string","description":"Fuente del aviso (gtfs-rt:renfe...)"},"text":{"type":"string"},"lines":{"type":"array","items":{"type":"string"},"description":"Lineas comerciales afectadas (C4, R2...), si se conocen"},"routeIds":{"type":"array","items":{"type":"string"}},"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"firstSeen":{"description":"Primera vez que el radar vio el aviso en el feed (si el operador no publica inicio)","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["id","source","text","lines","routeIds"],"additionalProperties":false,"description":"Aviso de servicio publicado por un operador (obras, cancelaciones, incidencias)"},"PushConfig":{"type":"object","properties":{"enabled":{"type":"boolean","description":"false sin claves VAPID o sin base de datos"},"publicKey":{"description":"Clave publica VAPID (applicationServerKey)","type":"string"},"maxDelayMin":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"maxArrivalMin":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["enabled","maxDelayMin","maxArrivalMin"],"additionalProperties":false,"description":"Disponibilidad de las notificaciones push y su clave publica"},"PushSubscriptionRequest":{"type":"object","properties":{"subscription":{"type":"object","properties":{"endpoint":{"type":"string","maxLength":2048,"format":"uri"},"keys":{"type":"object","properties":{"p256dh":{"type":"string","minLength":16,"maxLength":256},"auth":{"type":"string","minLength":8,"maxLength":128}},"required":["p256dh","auth"],"additionalProperties":false}},"required":["endpoint","keys"],"additionalProperties":false},"trainKey":{"type":"string","minLength":1,"maxLength":200,"description":"Clave del tren que se sigue"},"lang":{"type":"string","enum":["es","en"]},"delayMin":{"description":"Avisar si el retraso llega a estos minutos (5)","type":"integer","minimum":1,"maximum":120},"arrivalMin":{"description":"Avisar cuando falten estos minutos para llegar (5)","type":"integer","minimum":1,"maximum":60}},"required":["subscription","trainKey"],"additionalProperties":false,"description":"Suscripcion PushSubscription.toJSON() del navegador mas el tren a seguir"},"PushSubscriptionResult":{"type":"object","properties":{"id":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"trainKey":{"type":"string"},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"La suscripcion se borra sola a las 24 h"}},"required":["id","trainKey","expiresAt"],"additionalProperties":false},"PushUnsubscribe":{"type":"object","properties":{"endpoint":{"type":"string","maxLength":2048,"format":"uri"}},"required":["endpoint"],"additionalProperties":false},"DelayCertificate":{"type":"object","properties":{"version":{"type":"number","const":1},"id":{"type":"string","description":"Hash SHA-256 (20 hex) del contenido: cambia si se altera algo"},"issuedAt":{"type":"string"},"issuer":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"}},"required":["name","url"],"additionalProperties":false},"train":{"type":"object","properties":{"key":{"type":"string"},"circulation":{"type":"string"},"number":{"type":["string","null"]},"operator":{"type":["string","null"]},"category":{"type":["string","null"]},"series":{"type":["string","null"]},"line":{"type":["string","null"]}},"required":["key","circulation","number","operator","category","series","line"],"additionalProperties":false},"serviceDate":{"type":"string","description":"Dia de servicio AAAA-MM-DD"},"origin":{"type":["string","null"]},"destination":{"type":["string","null"]},"arrival":{"type":"object","properties":{"station":{"type":["string","null"]},"scheduled":{"type":["string","null"]},"actual":{"type":["string","null"]},"estimated":{"type":["string","null"]},"delayS":{"type":["number","null"]},"basis":{"type":"string","enum":["actual","estimated","last_known","none"],"description":"De donde sale el retraso en destino"}},"required":["station","scheduled","actual","estimated","delayS","basis"],"additionalProperties":false},"maxDelayS":{"type":["number","null"]},"cancelled":{"type":"boolean"},"stops":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"commercial":{"type":"boolean"},"cancelled":{"type":"boolean"},"track":{"type":["string","null"]},"scheduledArrival":{"type":["string","null"]},"scheduledDeparture":{"type":["string","null"]},"actualArrival":{"type":["string","null"]},"actualDeparture":{"type":["string","null"]},"estimatedArrival":{"type":["string","null"]},"delayS":{"type":["number","null"]}},"required":["name","commercial","cancelled","track","scheduledArrival","scheduledDeparture","actualArrival","actualDeparture","estimatedArrival","delayS"],"additionalProperties":false}},"observed":{"type":"object","properties":{"firstSeen":{"type":"string"},"lastSeen":{"type":"string"},"sources":{"type":"array","items":{"type":"string"}}},"required":["firstSeen","lastSeen","sources"],"additionalProperties":false},"disclaimer":{"type":"string"},"signature":{"anyOf":[{"type":"object","properties":{"algorithm":{"type":"string","const":"HMAC-SHA256"},"value":{"type":"string"}},"required":["algorithm","value"],"additionalProperties":false},{"type":"null"}]}},"required":["version","id","issuedAt","issuer","train","serviceDate","origin","destination","arrival","maxDelayS","cancelled","stops","observed","disclaimer","signature"],"additionalProperties":false,"description":"Certificado de retraso de una circulacion, verificable en /api/v1/certificates/verify"},"StationHistory":{"type":"object","properties":{"station":{"type":"object","properties":{"key":{"type":"string","description":"Clave estable: \"<fuente>:<id>\", p. ej. \"gtfs-rt:renfe:17000\""},"id":{"type":"string"},"source":{"type":"string","description":"Fuente que la publica (digitraffic, gtfs-rt:renfe, gtfs-rt:renfe-ld...)"},"name":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"upcoming":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["key","id","source","name","lat","lon","upcoming"],"additionalProperties":false,"description":"Estacion conocida por los horarios cargados o por las fuentes"},"days":{"type":"number"},"timeZone":{"type":"string"},"stationIds":{"type":"array","items":{"type":"string"},"description":"Ids de parada agregados (misma estacion en varias fuentes)"},"summary":{"type":"object","properties":{"n":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Pasos con retraso conocido (sin contar cancelados)"},"onTimePct":{"description":"% de pasos con retraso <= 5 min","type":["number","null"]},"avgDelayS":{"type":["number","null"]},"p90DelayS":{"type":["number","null"]},"maxDelayS":{"type":["number","null"]},"cancelled":{"type":"integer","minimum":0,"maximum":9007199254740991},"arrivals":{"type":"integer","minimum":0,"maximum":9007199254740991},"departures":{"type":"integer","minimum":0,"maximum":9007199254740991},"avgDwellS":{"type":["number","null"]}},"required":["n","onTimePct","avgDelayS","p90DelayS","maxDelayS","cancelled","arrivals","departures","avgDwellS"],"additionalProperties":false},"byHour":{"type":"array","items":{"type":"object","properties":{"n":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Pasos con retraso conocido (sin contar cancelados)"},"onTimePct":{"description":"% de pasos con retraso <= 5 min","type":["number","null"]},"avgDelayS":{"type":["number","null"]},"p90DelayS":{"type":["number","null"]},"maxDelayS":{"type":["number","null"]},"cancelled":{"type":"integer","minimum":0,"maximum":9007199254740991},"hour":{"type":"integer","minimum":0,"maximum":23}},"required":["n","onTimePct","avgDelayS","p90DelayS","maxDelayS","cancelled","hour"],"additionalProperties":false}},"byDay":{"type":"array","items":{"type":"object","properties":{"n":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Pasos con retraso conocido (sin contar cancelados)"},"onTimePct":{"description":"% de pasos con retraso <= 5 min","type":["number","null"]},"avgDelayS":{"type":["number","null"]},"p90DelayS":{"type":["number","null"]},"maxDelayS":{"type":["number","null"]},"cancelled":{"type":"integer","minimum":0,"maximum":9007199254740991},"date":{"type":"string"}},"required":["n","onTimePct","avgDelayS","p90DelayS","maxDelayS","cancelled","date"],"additionalProperties":false}},"events":{"type":"array","items":{"type":"object","properties":{"trainKey":{"type":"string"},"number":{"type":["string","null"]},"line":{"type":["string","null"]},"category":{"type":["string","null"]},"origin":{"type":["string","null"]},"destination":{"type":["string","null"]},"serviceDate":{"type":"string"},"kind":{"type":"string","enum":["arrival","departure","stop","pass"]},"scheduledArrival":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"scheduledDeparture":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"actualArrival":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"actualDeparture":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"delayS":{"type":["number","null"]},"delayBase":{"anyOf":[{"type":"string","enum":["actual","reported"]},{"type":"null"}],"description":"actual: hora real menos prevista; reported: ultimo retraso publicado en esa parada"},"dwellS":{"description":"Tiempo real en la estacion","type":["number","null"]},"track":{"type":["string","null"]},"cancelled":{"type":"boolean"}},"required":["trainKey","number","line","category","origin","destination","serviceDate","kind","scheduledArrival","scheduledDeparture","actualArrival","actualDeparture","delayS","delayBase","dwellS","track","cancelled"],"additionalProperties":false,"description":"Paso pasado de un tren por la estacion"},"description":"Ultimos pasos, del mas reciente al mas antiguo"}},"required":["station","days","timeZone","stationIds","summary","byHour","byDay","events"],"additionalProperties":false,"description":"Historico de llegadas, salidas y puntualidad de una estacion (base de datos de la Fase 16)"},"CertificateVerification":{"type":"object","properties":{"intact":{"type":"boolean","description":"El id coincide con el contenido"},"signatureValid":{"description":"Firma correcta de este servidor; null sin firma o sin secreto","type":["boolean","null"]},"id":{"type":"string"}},"required":["intact","signatureValid","id"],"additionalProperties":false},"PunctualityReport":{"type":"object","properties":{"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"from":{"type":"string"},"to":{"type":"string"},"by":{"type":"string","enum":["operator","category","line","train","station"]},"onTimeThresholdS":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"circulations":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Circulaciones en la ventana"},"groups":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"circulations":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"measured":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Circulaciones no canceladas con retraso en destino conocido"},"cancelled":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cancelledPct":{"type":"number"},"onTimePct":{"description":"% de medidas con retraso <= onTimeThresholdS","type":["number","null"]},"meanDelayS":{"description":"Retraso medio en destino (solo positivos), s","type":["number","null"]},"p90DelayS":{"type":["number","null"]},"maxDelayS":{"type":["number","null"]},"worst":{"anyOf":[{"type":"object","properties":{"circulation":{"type":"string"},"delayS":{"type":"number"}},"required":["circulation","delayS"],"additionalProperties":false},{"type":"null"}]}},"required":["key","name","circulations","measured","cancelled","cancelledPct","onTimePct","meanDelayS","p90DelayS","maxDelayS","worst"],"additionalProperties":false}}},"required":["days","from","to","by","onTimeThresholdS","circulations","groups"],"additionalProperties":false,"description":"Puntualidad historica agrupada, calculada sobre el historico guardado"},"AlertStats":{"type":"object","properties":{"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"from":{"type":"string"},"to":{"type":"string"},"coveredHours":{"type":"number","description":"Horas con historico dentro de la ventana (menos que la ventana si el historico es reciente)"},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bySeverity":{"type":"object","properties":{"critical":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"warning":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"info":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["critical","warning","info"],"additionalProperties":false},"criticalPerHour":{"type":"number","description":"Alertas criticas por hora cubierta"},"alertsPerHour":{"type":"number"},"resolvedPct":{"description":"% de alertas con resolucion registrada","type":["number","null"]},"medianLifeS":{"description":"Vida mediana de las resueltas, s","type":["number","null"]},"byKind":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"severity":{"type":"string"},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"resolved":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"medianLifeS":{"type":["number","null"]}},"required":["kind","severity","total","resolved","medianLifeS"],"additionalProperties":false}},"perDay":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"critical":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["date","total","critical"],"additionalProperties":false}},"byFeed":{"type":"array","items":{"type":"object","properties":{"feed":{"type":"string"},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"critical":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["feed","total","critical"],"additionalProperties":false}},"recentCritical":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"severity":{"type":"string"},"trains":{"type":"array","items":{"type":"string"}},"message":{"type":"string"},"evidence":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["number","string"]}},"createdAt":{"type":"string"},"resolvedAt":{"type":["string","null"]}},"required":["id","kind","severity","trains","message","evidence","createdAt","resolvedAt"],"additionalProperties":false}}},"required":["days","from","to","coveredHours","total","bySeverity","criticalPerHour","alertsPerHour","resolvedPct","medianLifeS","byKind","perDay","byFeed","recentCritical"],"additionalProperties":false,"description":"Estadisticas de las alertas del motor guardadas en el historico"},"AlertHistory":{"type":"object","properties":{"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"from":{"type":"string"},"to":{"type":"string"},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"alerts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"severity":{"type":"string"},"trains":{"type":"array","items":{"type":"string"}},"message":{"type":"string"},"evidence":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["number","string"]}},"createdAt":{"type":"string"},"resolvedAt":{"type":["string","null"]},"position":{"anyOf":[{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"}},"required":["lat","lon"],"additionalProperties":false},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["id","kind","severity","trains","message","evidence","createdAt","resolvedAt","position","updatedAt"],"additionalProperties":false}}},"required":["days","from","to","count","offset","alerts"],"additionalProperties":false},"NetworkDaily":{"type":"object","properties":{"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"timeZone":{"type":"string"},"total":{"type":"object","properties":{"circulations":{"type":"integer","minimum":0,"maximum":9007199254740991},"measured":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Con retraso en destino conocido (sin cancelados)"},"onTime":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Retraso <= 5 min"},"minor":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Leve: 5-15 min"},"moderate":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moderado: 15-30 min"},"severe":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Severo: mas de 30 min"},"cancelled":{"type":"integer","minimum":0,"maximum":9007199254740991},"onTimePct":{"type":["number","null"]},"meanDelayS":{"description":"Media del retraso positivo en destino","type":["number","null"]}},"required":["circulations","measured","onTime","minor","moderate","severe","cancelled","onTimePct","meanDelayS"],"additionalProperties":false,"description":"Circulaciones por gravedad del retraso en destino"},"daily":{"type":"array","items":{"type":"object","properties":{"circulations":{"type":"integer","minimum":0,"maximum":9007199254740991},"measured":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Con retraso en destino conocido (sin cancelados)"},"onTime":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Retraso <= 5 min"},"minor":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Leve: 5-15 min"},"moderate":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moderado: 15-30 min"},"severe":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Severo: mas de 30 min"},"cancelled":{"type":"integer","minimum":0,"maximum":9007199254740991},"onTimePct":{"type":["number","null"]},"meanDelayS":{"description":"Media del retraso positivo en destino","type":["number","null"]},"date":{"type":"string"}},"required":["circulations","measured","onTime","minor","moderate","severe","cancelled","onTimePct","meanDelayS","date"],"additionalProperties":false}},"topToday":{"type":"array","items":{"type":"object","properties":{"trainKey":{"type":"string"},"number":{"type":["string","null"]},"category":{"type":["string","null"]},"origin":{"type":["string","null"]},"destination":{"type":["string","null"]},"delayS":{"type":"number"}},"required":["trainKey","number","category","origin","destination","delayS"],"additionalProperties":false},"description":"Mayores retrasos en destino del dia de servicio de hoy"}},"required":["days","timeZone","total","daily","topToday"],"additionalProperties":false,"description":"Evolucion diaria de la puntualidad de la red (Fase 24)"},"CorridorList":{"type":"object","properties":{"serverTime":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"history":{"type":"boolean","description":"false: sin base de datos, solo datos en vivo"},"corridors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ends":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"}],"items":false,"minItems":2,"maxItems":2,"description":"Nombre de cada extremo"},"live":{"type":"object","properties":{"trains":{"type":"integer","minimum":0,"maximum":9007199254740991},"outbound":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Del primer extremo al segundo"},"inbound":{"type":"integer","minimum":0,"maximum":9007199254740991},"onTimePct":{"type":["number","null"]},"meanDelayS":{"type":["number","null"]},"worst":{"anyOf":[{"type":"object","properties":{"trainKey":{"type":"string"},"number":{"type":"string"},"delayS":{"type":"number"}},"required":["trainKey","delayS"],"additionalProperties":false},{"type":"null"}]},"trainKeys":{"type":"array","items":{"type":"string"}}},"required":["trains","outbound","inbound","onTimePct","meanDelayS","worst","trainKeys"],"additionalProperties":false},"week":{"anyOf":[{"type":"object","properties":{"circulations":{"type":"integer","minimum":0,"maximum":9007199254740991},"measured":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Con retraso en destino conocido (sin cancelados)"},"onTime":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Retraso <= 5 min"},"minor":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Leve: 5-15 min"},"moderate":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moderado: 15-30 min"},"severe":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Severo: mas de 30 min"},"cancelled":{"type":"integer","minimum":0,"maximum":9007199254740991},"onTimePct":{"type":["number","null"]},"meanDelayS":{"description":"Media del retraso positivo en destino","type":["number","null"]}},"required":["circulations","measured","onTime","minor","moderate","severe","cancelled","onTimePct","meanDelayS"],"additionalProperties":false,"description":"Circulaciones por gravedad del retraso en destino"},{"type":"null"}],"description":"Ultimos 7 dias; null sin base de datos"},"previousWeek":{"anyOf":[{"type":"object","properties":{"circulations":{"type":"integer","minimum":0,"maximum":9007199254740991},"measured":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Con retraso en destino conocido (sin cancelados)"},"onTime":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Retraso <= 5 min"},"minor":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Leve: 5-15 min"},"moderate":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moderado: 15-30 min"},"severe":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Severo: mas de 30 min"},"cancelled":{"type":"integer","minimum":0,"maximum":9007199254740991},"onTimePct":{"type":["number","null"]},"meanDelayS":{"description":"Media del retraso positivo en destino","type":["number","null"]}},"required":["circulations","measured","onTime","minor","moderate","severe","cancelled","onTimePct","meanDelayS"],"additionalProperties":false,"description":"Circulaciones por gravedad del retraso en destino"},{"type":"null"}],"description":"Los 7 dias anteriores"}},"required":["id","name","ends","live","week","previousWeek"],"additionalProperties":false,"description":"Corredor ferroviario: trenes en vivo y puntualidad frente a la semana anterior"}}},"required":["serverTime","history","corridors"],"additionalProperties":false},"TrainPunctuality":{"type":"object","properties":{"trainKey":{"type":"string"},"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"from":{"type":"string"},"to":{"type":"string"},"onTimeThresholdS":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"circulations":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"measured":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cancelled":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"onTimePct":{"type":["number","null"]},"meanDelayS":{"type":["number","null"]},"p90DelayS":{"type":["number","null"]},"recent":{"type":"array","items":{"type":"object","properties":{"circulation":{"type":"string"},"serviceDate":{"type":"string"},"delayS":{"type":["number","null"]},"cancelled":{"type":"boolean"}},"required":["circulation","serviceDate","delayS","cancelled"],"additionalProperties":false}}},"required":["trainKey","days","from","to","onTimeThresholdS","circulations","measured","cancelled","onTimePct","meanDelayS","p90DelayS","recent"],"additionalProperties":false,"description":"Puntualidad de un tren en la ventana, con sus ultimas circulaciones"},"Health":{"type":"object","properties":{"status":{"type":"string","const":"ok"},"protocol":{"type":"number","const":1},"version":{"type":"string"},"uptimeS":{"type":"number","minimum":0},"trains":{"type":"integer","minimum":0,"maximum":9007199254740991},"sources":{"type":"integer","minimum":0,"maximum":9007199254740991},"clients":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["status","protocol","version","uptimeS","trains","sources","clients"],"additionalProperties":false},"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"],"additionalProperties":false}}}}