{"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":"GPL-3.0-only","url":"https://www.gnu.org/licenses/gpl-3.0.html"}},"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":"sistema","description":"Salud, fuentes y documentacion"}],"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`. Los `source.kind` internos (simulator, digitraffic, gtfs-rt) estan reservados; `operator` exige la cabecera X-Source-Key.","parameters":[{"name":"X-Source-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Clave de fuente opcional (TR_SOURCE_KEYS). Sube la confianza a operator."}],"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 desconocida","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/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/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/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: observations (lote), 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"],"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},"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"],"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},"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"},"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"]},"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"],"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"}},"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"},"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"]},"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"],"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"}},"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},"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"],"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},"trains":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["id","kind","name","enabled","healthy","lastSuccessAt","lastError","observations","trains"],"additionalProperties":false,"description":"Estado de una fuente conectada"},"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}}}}