{"openapi":"3.0.0","info":{"title":"Cambio Uruguay API","version":"1.0.0","description":"\n# Cambio Uruguay API\n\nAPI completa para obtener tipos de cambio y información de casas de cambio en Uruguay.\n\nEsta API proporciona:\n- Tipos de cambio actualizados en tiempo real\n- Cotizaciones preferenciales por proveedor, monto e histórico diario\n- Información de ubicaciones de casas de cambio\n- Datos históricos de evolución de monedas\n- Cotizaciones regionales (Argentina, Brasil, Paraguay, Chile y Bolivia) con series diarias\n- Búsqueda por geolocalización\n- Información del Banco Central del Uruguay (BCU)\n\n## Base URL\n\n**Producción:** `https://api.cambio-uruguay.com`\n\n## Formatos de respuesta\n\nTodas las respuestas de la API están en formato JSON.\n\n## Zona horaria\n\nTodos los timestamps y fechas están en la zona horaria de Uruguay (America/Montevideo).\n    ","contact":{"name":"Cambio Uruguay","url":"https://cambio-uruguay.com","email":"info@cambio-uruguay.com"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"},"termsOfService":"https://cambio-uruguay.com/terminos"},"servers":[{"url":"https://api.cambio-uruguay.com","description":"Servidor de Producción"},{"url":"http://localhost:3528","description":"Servidor de Desarrollo"}],"tags":[{"name":"Exchange Data","description":"Endpoints para obtener datos de tipos de cambio"},{"name":"Locations","description":"Endpoints para obtener información de ubicaciones"},{"name":"Health","description":"Endpoints para verificar el estado del servicio"},{"name":"BCU","description":"Endpoints para datos del Banco Central del Uruguay"},{"name":"Evolution","description":"Endpoints para datos históricos y evolución de monedas"},{"name":"Geocoding","description":"Endpoints para servicios de geocodificación"},{"name":"Parameters","description":"Endpoints para obtener parámetros válidos del sistema"},{"name":"Regional","description":"Cotizaciones de la región: el dólar y los cruces en Argentina, Brasil, Paraguay, Chile, Bolivia y Uruguay, con series diarias y comparación de rutas"},{"name":"Aduana","description":"Guía de problemas con la aduana uruguaya: hechos legales citados, procedimientos y testimonios de r/uruguay"},{"name":"Debug","description":"Endpoints de depuración para monitorear el estado interno del servicio"},{"name":"AI Insights","description":"Endpoints de inteligencia artificial para análisis de mercado y generación de insights"}],"components":{"schemas":{"ExchangeData":{"type":"object","properties":{"origin":{"type":"string","description":"Nombre de la casa de cambio","example":"la_favorita"},"code":{"type":"string","description":"Código de la moneda (ISO 4217)","example":"USD"},"type":{"type":"string","description":"Tipo de cambio (BILLETE, CABLE, etc.)","example":""},"buy":{"type":"number","format":"float","description":"Precio de compra","example":38.75},"sell":{"type":"number","format":"float","description":"Precio de venta","example":41.15},"date":{"type":"string","format":"date-time","description":"Fecha y hora de la cotización","example":"2025-08-09T03:00:00.000Z"},"name":{"type":"string","description":"Nombre descriptivo de la moneda","example":"Dólar Estadounidense"}},"required":["origin","code","buy","sell","date"]},"Location":{"type":"object","properties":{"name":{"type":"string","description":"Nombre de la sucursal","example":"Cambio Maiorano Centro"},"address":{"type":"string","description":"Dirección de la sucursal","example":"Av. 18 de Julio 1234"},"latitude":{"type":"number","format":"float","description":"Latitud geográfica","example":-34.9173},"longitude":{"type":"number","format":"float","description":"Longitud geográfica","example":-56.1501},"department":{"type":"string","description":"Departamento de Uruguay","example":"Montevideo"},"city":{"type":"string","description":"Ciudad","example":"Montevideo"},"distance":{"type":"number","format":"float","description":"Distancia en metros (solo si se proporciona ubicación)","example":1250.5}},"required":["name","address","latitude","longitude"]},"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded","error"],"description":"Estado del servicio (ok, degraded, error)","example":"ok"},"message":{"type":"string","description":"Mensaje descriptivo del estado","example":"Sync is recent"},"timestamp":{"type":"string","format":"date-time","description":"Timestamp de la respuesta","example":"2024-08-09T12:00:00.000Z"},"uptime":{"type":"number","description":"Tiempo de actividad del servidor en segundos","example":3600},"database":{"type":"object","properties":{"connected":{"type":"boolean","description":"Si la base de datos está conectada"},"readyState":{"type":"number","description":"Estado de la conexión (0=disconnected, 1=connected, 2=connecting, 3=disconnecting)"},"readyStateText":{"type":"string","description":"Descripción del estado de conexión"}}},"sync":{"type":"object","properties":{"available":{"type":"boolean","description":"Si hay información de sincronización disponible"},"lastSync":{"type":"string","format":"date-time","description":"Última sincronización exitosa"},"minutesAgo":{"type":"number","description":"Minutos desde la última sincronización"}}}},"required":["status","message","timestamp"]},"AIInsightResponse":{"type":"object","properties":{"insight":{"type":"string","description":"Análisis generado por IA en formato Markdown","example":"## 📊 Resumen del Mercado\n\nEl dólar se cotiza hoy entre..."},"type":{"type":"string","enum":["market_summary","currency_analysis","best_rates","trend_analysis","custom"],"description":"Tipo de análisis generado"},"timestamp":{"type":"string","format":"date-time","description":"Momento en que se generó el análisis"},"language":{"type":"string","enum":["es","en","pt"],"description":"Idioma del análisis"},"cached":{"type":"boolean","description":"Si la respuesta provino de la caché"}},"required":["insight","type","timestamp","language","cached"]},"PingResponse":{"type":"object","properties":{"expected":{"type":"boolean","description":"Si la respuesta es la esperada (>= 100 registros)","example":true},"total":{"type":"number","description":"Cantidad total de registros devueltos","example":150}},"required":["expected","total"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","description":"Mensaje de error","example":"No results found"}},"required":["error"]},"GeocodeResponse":{"type":"object","properties":{"display_name":{"type":"string","description":"Nombre completo del lugar","example":"Montevideo, Uruguay"},"lat":{"type":"string","description":"Latitud","example":"-34.9011127"},"lon":{"type":"string","description":"Longitud","example":"-56.1645314"},"boundingbox":{"type":"array","items":{"type":"string"},"description":"Límites geográficos","example":["-34.9411127","-34.8611127","-56.2045314","-56.1245314"]}},"required":["display_name","lat","lon"]},"CurrencyEvolution":{"type":"object","properties":{"date":{"type":"string","format":"date","description":"Fecha de la cotización","example":"2025-08-09"},"buy":{"type":"number","format":"float","description":"Precio de compra","example":38.75},"sell":{"type":"number","format":"float","description":"Precio de venta","example":41.15},"avg":{"type":"number","format":"float","description":"Precio promedio","example":39.95}},"required":["date","buy","sell"]},"ParametersResponse":{"type":"object","properties":{"origins":{"type":"array","items":{"type":"string"},"description":"Lista de casas de cambio disponibles"},"currencies":{"type":"array","items":{"type":"string"},"description":"Lista de códigos de moneda disponibles"},"types":{"type":"array","items":{"type":"string"},"description":"Lista de tipos de cambio disponibles"},"locations":{"type":"array","items":{"type":"string"},"description":"Lista de ubicaciones disponibles"},"counts":{"type":"object","properties":{"origins":{"type":"integer","description":"Número total de casas de cambio"},"currencies":{"type":"integer","description":"Número total de monedas"},"types":{"type":"integer","description":"Número total de tipos"},"locations":{"type":"integer","description":"Número total de ubicaciones"}}}}},"ValidationError":{"type":"object","properties":{"error":{"type":"string","description":"Mensaje de error de validación"},"parameter":{"type":"string","description":"Nombre del parámetro inválido"},"value":{"type":"string","description":"Valor proporcionado que es inválido"},"validValues":{"type":"array","items":{"type":"string"},"description":"Lista de valores válidos para este parámetro"},"suggestion":{"type":"string","description":"Sugerencia para corregir el error"}},"required":["error","parameter","validValues"]}},"parameters":{"DateParam":{"name":"date","in":"query","description":"Fecha en formato YYYY-MM-DD (zona horaria de Uruguay)","required":false,"schema":{"type":"string","format":"date","example":"2025-08-09"}},"LatitudeParam":{"name":"latitude","in":"query","description":"Latitud geográfica para calcular distancias","required":false,"schema":{"type":"number","format":"float","minimum":-90,"maximum":90,"example":-34.9173}},"LongitudeParam":{"name":"longitude","in":"query","description":"Longitud geográfica para calcular distancias","required":false,"schema":{"type":"number","format":"float","minimum":-180,"maximum":180,"example":-56.1501}},"PeriodParam":{"name":"period","in":"query","description":"Período en meses para datos históricos (1-60)","required":false,"schema":{"type":"integer","minimum":1,"maximum":60,"default":6,"example":12}},"OriginParam":{"name":"origin","in":"path","description":"Casa de cambio. Para obtener valores válidos, consulte /parameters/origins","required":true,"schema":{"type":"string","example":"la_favorita"},"x-parameter-type":"origin"},"CurrencyCodeParam":{"name":"code","in":"path","description":"Código de moneda (ISO 4217). Para obtener valores válidos, consulte /parameters/currencies","required":true,"schema":{"type":"string","pattern":"^[A-Z]{3}$","example":"USD"},"x-parameter-type":"currency"},"ExchangeTypeParam":{"name":"type","in":"path","description":"Tipo de cambio. Para obtener valores válidos, consulte /parameters/types","required":true,"schema":{"type":"string","example":""},"x-parameter-type":"type"},"LocationParam":{"name":"location","in":"path","description":"Ubicación o departamento. Para obtener valores válidos, consulte /parameters/locations","required":true,"schema":{"type":"string","example":"Montevideo"},"x-parameter-type":"location"}},"responses":{"BadRequest":{"description":"Solicitud incorrecta","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Recurso no encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalError":{"description":"Error interno del servidor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ValidationError":{"description":"Error de validación de parámetros","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"examples":{"invalid_origin":{"summary":"Casa de cambio inválida","value":{"error":"Invalid origin parameter","parameter":"origin","value":"invalid_exchange","validValues":["la_favorita","cambio_minas","brou","cambio_regul","itau","oca","prex","santander","bcu","cambilex"],"suggestion":"Use /parameters/origins to get all valid origins"}},"invalid_currency":{"summary":"Moneda inválida","value":{"error":"Invalid currency code","parameter":"code","value":"INVALID","validValues":["USD","EUR","ARS","BRL","XAU","UR","UP","UI","PYG","PEN","MXN","JPY","GBP","COP","CLP","CHF","CAD","AUD"],"suggestion":"Use /parameters/currencies to get all valid currency codes"}}}}}}}},"paths":{"/":{"get":{"tags":["Exchange Data"],"summary":"Obtener todos los tipos de cambio","description":"Retorna todos los tipos de cambio disponibles para la fecha especificada.\nSi no se especifica fecha, retorna los tipos de cambio más recientes.\n\nLos datos incluyen información de múltiples casas de cambio en Uruguay,\ncon precios de compra y venta para diferentes monedas.\n","parameters":[{"$ref":"#/components/parameters/DateParam"}],"responses":{"200":{"description":"Lista de tipos de cambio exitosa","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeData"}},"examples":{"success":{"summary":"Respuesta exitosa","value":[{"origin":"la_favorita","code":"USD","type":"","buy":38.75,"sell":41.15,"date":"2025-08-09T03:00:00.000Z","name":"Dólar Estadounidense"},{"origin":"brou","code":"EUR","type":"","buy":44.07,"sell":49.45,"date":"2025-08-09T03:00:00.000Z","name":"Euro"}]}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/changes":{"get":{"tags":["Evolution"],"summary":"Últimos cambios reales de cotización","description":"Devuelve únicamente transiciones donde cambió compra o venta; las verificaciones sin cambios no generan registros.","parameters":[{"name":"origin","in":"query","schema":{"type":"string"}},{"name":"code","in":"query","schema":{"type":"string","example":"USD"}},{"name":"type","in":"query","schema":{"type":"string"}},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}}],"responses":{"200":{"description":"Cambios ordenados del más reciente al más antiguo"}}}},"/market-change":{"get":{"tags":["Evolution"],"summary":"Variación del mercado contra una hora exacta","description":"Compara el promedio actual con el estado reconstruido exactamente N horas atrás (24 por defecto).","parameters":[{"name":"hours","in":"query","schema":{"type":"integer","minimum":1,"maximum":720,"default":24}}],"responses":{"200":{"description":"Variación por moneda usando las mismas cotizaciones en ambos extremos"}}}},"/intraday":{"get":{"tags":["Evolution"],"summary":"Cotización y variación intradía de un día concreto","description":"Devuelve, para un día calendario de Montevideo, la apertura, el último\nvalor, el máximo, el mínimo y la lista de cambios reales de cada\ncotización pública de una moneda.\n\nLa apertura no se lee de la fila diaria (esa fila se sobrescribe en cada\nsync, así que es el cierre): se reconstruye con el `previousBuy`/`previousSell`\ndel primer cambio del día y, si el día no tuvo cambios, con el último\nestado conocido anterior. `openSource` dice cuál de los caminos se usó.\n\nEndpoint público, sin autenticación y sin datos personales: sólo precios\npublicados por las casas de cambio.\n","parameters":[{"name":"date","in":"query","schema":{"type":"string","format":"date","example":"2026-08-20"},"description":"Día en zona America/Montevideo. Por defecto, hoy."},{"name":"code","in":"query","schema":{"type":"string","default":"USD","example":"USD"}},{"name":"origins","in":"query","schema":{"type":"string"},"description":"Lista de origins separada por comas."},{"name":"type","in":"query","schema":{"type":"string","example":"EBROU"},"description":"Tipo exacto de cotización. Vacío = cotización simple; hoy también existen EBROU y TRANSFERENCIA."}],"responses":{"200":{"description":"Serie intradía por cotización más el resumen del día"},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/regional":{"get":{"tags":["Regional"],"summary":"Tablero de cotizaciones de la región (AR, BR, PY, CL, BO, UY)","description":"Una foto del dólar en los seis países, con TODOS los mercados que cada\nuno publica: los siete dólares argentinos (oficial, blue, MEP, CCL,\nmayorista, cripto y tarjeta), el fixing PTAX y el dólar turismo\nbrasileños, el referencial y la planilla del Banco Central del\nParaguay, el dólar observado chileno, el oficial y el paralelo\nbolivianos, y el mejor precio entre las casas de cambio uruguayas.\n\nAdemás de las cotizaciones crudas devuelve lo derivado: la brecha\nentre el precio oficial y el que se consigue en cada país, la matriz\nde cruces regionales implícitos por el dólar, y la comparación de\nrutas (\"comprar la moneda acá\" contra \"llevar dólares y cambiarlos\nallá\") para cada vecino.\n\nCada cotización dice de qué fuente salió, cuándo la fijó esa fuente,\nqué tipo de precio es (`official`, `parallel`, `retail`, ...) y qué\notras fuentes publicaron ese mismo mercado (`corroboratedBy`) con la\ndiferencia máxima entre ellas (`disagreementPct`). `rejected` lista lo\nque el validador descartó y por qué.\n\nSe actualiza cada 20 minutos. Endpoint público, sin autenticación.\n","responses":{"200":{"description":"Snapshot regional completo","content":{"application/json":{"schema":{"type":"object","properties":{"generatedAt":{"type":"string","format":"date-time"},"oldestQuoteAt":{"type":"string","format":"date-time","nullable":true},"quotes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"AR:blue:USDARS"},"country":{"type":"string","enum":["AR","BR","CL","PY","BO","UY"]},"market":{"type":"string","example":"blue"},"label":{"type":"string","example":"Dólar blue"},"kind":{"type":"string","enum":["official","wholesale","parallel","financial","card","retail","reference"]},"base":{"type":"string","example":"USD"},"quote":{"type":"string","example":"ARS"},"buy":{"type":"number","nullable":true},"sell":{"type":"number","nullable":true},"avg":{"type":"number"},"spreadPct":{"type":"number","nullable":true},"variationPct":{"type":"number","nullable":true},"updatedAt":{"type":"string","format":"date-time"},"source":{"type":"string","example":"ar_bluelytics"},"sourceUrl":{"type":"string"},"corroboratedBy":{"type":"array","items":{"type":"string"}},"disagreementPct":{"type":"number","nullable":true}}}},"board":{"type":"array","items":{"type":"object"}},"gaps":{"type":"array","items":{"type":"object"}},"cross":{"type":"array","items":{"type":"object"}},"routes":{"type":"array","items":{"type":"object"}},"sources":{"type":"array","items":{"type":"object"}},"rejected":{"type":"array","items":{"type":"object"}}}}}}}}}},"/regional/sources":{"get":{"tags":["Regional"],"summary":"Las fuentes del tablero regional, con el estado de cada una","description":"Quién publica cada número, con qué acceso (API documentada o scraping\nde HTML), qué aporta que no aporte ninguna otra — y **cómo le fue en la\núltima corrida**: si respondió, cuánto tardó, qué mercados terminó\npublicando, en cuáles quedó como corroboración de otra, qué se le\ndescartó y con qué motivo.\n\nLos feeds mundiales traen `country: null` y `scope: \"global\"`: no\ndescriben a un país en particular y ponerles uno era engañoso.\n\n`summary.singleSourceMarkets` es el número que importa vigilar: son los\nmercados que hoy dependen de una sola lectura.\n","responses":{"200":{"description":"Catálogo y estado de las fuentes"}}}},"/regional/sources/{id}":{"get":{"tags":["Regional"],"summary":"Una sola fuente, con todo lo que se sabe de ella","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","example":"py_bcp"}}],"responses":{"200":{"description":"Ficha de la fuente"},"404":{"description":"No existe una fuente con ese id"}}}},"/regional/changes":{"get":{"tags":["Regional"],"summary":"Cada movimiento de precio del tablero regional","description":"Una fila por cada vez que un precio cambió, **sin umbral mínimo**: un\nguaraní, un centavo de real o una diezmilésima de peso entran igual.\n\nEs la otra mitad de `/regional/history`: la fila diaria guarda el\ncierre y se sobrescribe en cada corrida, así que lo que pasa adentro de\nun día sólo existe acá.\n\nLa RESOLUCIÓN es la del trabajo que lo alimenta: se ve lo que hay\ncuando se mira, cada diez minutos. Dos movimientos dentro de la misma\nventana quedan registrados como uno, del primer valor al último; por\neso cada fila trae `observedAt` (cuándo lo vimos), `sourceUpdatedAt`\n(cuándo dice la fuente que se fijó) y `sinceMinutes` (cuánto pasó desde\nla lectura anterior de ese mercado).\n","parameters":[{"name":"key","in":"query","schema":{"type":"string","example":"AR:blue:USDARS"},"description":"Clave exacta del mercado"},{"name":"country","in":"query","schema":{"type":"string","enum":["AR","BR","CL","PY","BO","UY"]}},{"name":"market","in":"query","schema":{"type":"string","example":"blue"}},{"name":"base","in":"query","schema":{"type":"string","example":"USD"}},{"name":"quote","in":"query","schema":{"type":"string","example":"ARS"}},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":5000,"default":500}}],"responses":{"200":{"description":"Movimientos, del más nuevo al más viejo"},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/regional/series":{"get":{"tags":["Regional"],"summary":"Qué series históricas existen y desde cuándo","description":"Una fila por serie almacenada, con la cantidad de días y el rango de\nfechas. Las argentinas arrancan en 2011 y la brasileña antes; las que\nningún tercero publica (Paraguay, Bolivia, el tablero uruguayo)\narrancan el día que este trabajo corrió por primera vez.\n","responses":{"200":{"description":"Catálogo de series"}}}},"/regional/history":{"get":{"tags":["Regional"],"summary":"Serie diaria de un mercado regional","description":"Devuelve un punto por día (cierre) filtrando por país, mercado y par.\nLas series con historia propia del publicador llegan hasta 2011\n(dólares argentinos) o antes (PTAX brasileño); el resto empieza el día\nque este tablero se publicó.\n\nSin filtros devuelve TODAS las series mezcladas, lo cual casi nunca es\nlo que se quiere: filtrar por `country` + `market` es lo habitual.\n","parameters":[{"name":"country","in":"query","schema":{"type":"string","enum":["AR","BR","CL","PY","BO","UY"]}},{"name":"market","in":"query","schema":{"type":"string","example":"blue"}},{"name":"base","in":"query","schema":{"type":"string","example":"USD"}},{"name":"quote","in":"query","schema":{"type":"string","example":"ARS"}},{"name":"from","in":"query","schema":{"type":"string","format":"date","example":"2024-01-01"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":20000,"default":5000}}],"responses":{"200":{"description":"Puntos diarios en orden cronológico"},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/regional/compare":{"get":{"tags":["Regional"],"summary":"¿Compro la moneda acá o llevo dólares?","description":"Para cada vecino compara dos rutas con precios reales: comprar la\nmoneda en una casa de cambio uruguaya, o comprar dólares acá y\ncambiarlos allá. Devuelve el costo de una unidad por cada ruta, cuál\ngana y por cuánto.\n\nCon `amount` devuelve además cuánto sale esa cantidad por cada ruta.\n","parameters":[{"name":"currency","in":"query","schema":{"type":"string","example":"ARS"},"description":"Limitar a una moneda (ARS, BRL, PYG, CLP, BOB)."},{"name":"amount","in":"query","schema":{"type":"number","example":10000},"description":"Cantidad de moneda extranjera a costear."}],"responses":{"200":{"description":"Comparación de rutas"},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/regional/convert":{"get":{"tags":["Regional"],"summary":"Convertir entre monedas de la región, pasando por el dólar","description":"El dólar es el pivote porque es la única moneda que los seis países\ncotizan directamente. `market` fija el mercado del lado de origen: la\ndiferencia entre convertir pesos argentinos al oficial y al blue es\njustamente el sentido del parámetro.\n\nLa respuesta incluye `legs`, los identificadores de las cotizaciones\nusadas, para que cualquier resultado se pueda rehacer a mano.\n","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","example":"ARS"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","example":"UYU"}},{"name":"amount","in":"query","schema":{"type":"number","default":1}},{"name":"market","in":"query","schema":{"type":"string","example":"blue"},"description":"Mercado del lado de origen (blue, oficial, ptax, turismo, ...)."}],"responses":{"200":{"description":"Resultado de la conversión con las cotizaciones usadas"},"400":{"$ref":"#/components/responses/ValidationError"},"404":{"description":"No hay cotizaciones para ese par"}}}},"/precios/articles":{"get":{"tags":["Precios"],"summary":"Catalogo de articulos del SIPC con las estadisticas del dia","description":"Los precios son los del SIPC (Sistema de Informacion de Precios al\nConsumidor, MEF / Area Defensa del Consumidor), la fuente oficial:\n`precios.gub.uy` responde 301 hacia `www.precios.uy`.\n\nLo que agrega esta API es el HISTORICO, que el origen no guarda: su\nAPI devuelve solo el precio de hoy con su fecha y no tiene endpoint de\nserie.\n","responses":{"200":{"description":"Catalogo con minimo, mediana y maximo nacional por articulo"}}}},"/precios/article/{id}":{"get":{"tags":["Precios"],"summary":"Precio de un articulo local por local, con su antiguedad","description":"Cada fila trae la FECHA que declara el origen. El 94 % son del dia o\ndel anterior, pero hay gondolas que no se actualizan desde hace\nsemanas, asi que `freshness` viene en cada fila y **`cheapest` nunca\nes una fila `stale` ni una marcada `suspect`** - ordenar por precio a\nsecas sube al titular la gondola mas vieja o el error de carga.\n\n`promo: true` significa que el origen declaro la celda como\n`oferta - $N`: es 1 de cada 10 filas y es lo que se paga hoy, asi que\nsi puede encabezar.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Filas por local, estadisticas del dia y serie propia"}}}},"/precios/basket":{"get":{"tags":["Precios"],"summary":"Canasta propia, nivel de precios por departamento y por cadena","description":"La canasta esta PINNEADA y versionada, y se calcula solo con\nobservaciones reales.\n\nEl orden es por canasta EMPAREJADA (`ratio`): lo que el local cobra\npor los articulos que declara, contra la mediana nacional de esos\nmismos articulos. No por el total, porque el total baja cuando al\nlocal le FALTAN articulos y no cuando es barato - medido, la\ncorrelacion entre cobertura y total crudo es 0,842 y de los diez mas\nbaratos por total solo uno sigue estando entre los diez mas baratos\npor canasta emparejada.\n\nUn local con menos del 70 % de la canasta no se rankea, y un\ndepartamento o cadena con menos de 5 locales calificados tampoco:\ndice `qualified: false`. No se publica ningun total completado.\n","responses":{"200":{"description":"Canasta del ultimo dia, con ambitos y veredictos de gondola"}}}},"/precios/stores":{"get":{"tags":["Precios"],"summary":"Locales que declaran precios, dentro de un radio","description":"Los 18 locales del catalogo que no traen coordenada NO pueden entrar\nen una consulta por radio: sin coordenada no hay distancia.\n","parameters":[{"name":"near","in":"query","required":true,"schema":{"type":"string","example":"-34.9011,-56.1645"}},{"name":"r","in":"query","schema":{"type":"number","default":5}}],"responses":{"200":{"description":"Locales ordenados por distancia"}}}},"/precios/changes":{"get":{"tags":["Precios"],"summary":"Cada cambio de precio, sin umbral minimo","description":"Es la mitad irreconstruible del archivo: la fila diaria se sobrescribe,\nasi que un movimiento solo existe si se escribio cuando paso. Se\nregistra toda diferencia contra la lectura anterior, por chica que sea.\n","parameters":[{"name":"articleId","in":"query","schema":{"type":"integer"}},{"name":"storeId","in":"query","schema":{"type":"integer"}},{"name":"day","in":"query","schema":{"type":"string","format":"date"}},{"name":"limit","in":"query","schema":{"type":"integer","default":500}}],"responses":{"200":{"description":"Movimientos de precio, del mas reciente al mas viejo"}}}},"/analytics/rates":{"get":{"tags":["Evolution"],"summary":"Series de cotización por hora o por día","description":"Reconstruye el último valor conocido de cada casa por intervalo, usando el ledger intradía y el histórico diario anterior.","parameters":[{"name":"code","in":"query","schema":{"type":"string","example":"USD"}},{"name":"origins","in":"query","schema":{"type":"string"},"description":"Lista de origins separada por comas"},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"interval","in":"query","schema":{"type":"string","enum":["hour","day"],"default":"hour"}}],"responses":{"200":{"description":"Series alineadas por intervalo y catálogo de filtros disponibles"}}}},"/analytics/branches":{"get":{"tags":["Evolution"],"summary":"Sucursales disponibles para filtrar analíticas","description":"Incluye sucursales activas aunque todavía no tengan coordenadas para el mapa.","responses":{"200":{"description":"Catálogo de sucursales activas"}}}},"/ping":{"get":{"tags":["Health"],"summary":"Verificar disponibilidad de datos","description":"Endpoint para verificar que la API tiene datos suficientes disponibles.\nRetorna información sobre la cantidad de registros disponibles y\nsi cumple con el umbral mínimo esperado (≥100 registros).\n","parameters":[{"$ref":"#/components/parameters/DateParam"}],"responses":{"200":{"description":"Verificación exitosa con datos suficientes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingResponse"},"examples":{"sufficient_data":{"summary":"Datos suficientes disponibles","value":{"expected":true,"total":150}}}}}},"500":{"description":"Datos insuficientes o error del servidor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingResponse"},"examples":{"insufficient_data":{"summary":"Datos insuficientes","value":{"expected":false,"total":50}}}}}}}}},"/frozen-quotes":{"get":{"tags":["Health"],"summary":"Pizarras que no cambian de precio","description":"Cotizaciones cuyo precio no se movió en 7 días o más, medido contra su propia historia.\n\nEs la cuarta guarda del pipeline y mira un eje que las otras tres no ven: `rate_plausibility`\ncompara compra contra venta por fila, `rate_audit` compara cada casa contra las demás, y el\nestado `stale` de /estado mira la FECHA de la fila. Un origen que publica fila fresca todos\nlos días con el mismo número pasa las tres.\n\nImporta porque el daño no es pasivo: el mercado se mueve y la pizarra quieta no, así que\nderiva al extremo de la distribución — y como el sitio ordena por \"más barato\", la sube al\ntitular. Por eso cada fila trae `extreme`, que dice si esa cotización encabeza hoy su grupo.\n\nLo calcula el job de sync al final de cada corrida y se sirve desde archivo: esta ruta no\ntoca la base.\n","responses":{"200":{"description":"Informe de pizarras congeladas","content":{"application/json":{"schema":{"type":"object","properties":{"generatedAt":{"type":"string","format":"date-time"},"windowDays":{"type":"number"},"checked":{"type":"number"},"quotes":{"type":"array","items":{"type":"object","properties":{"origin":{"type":"string"},"code":{"type":"string"},"type":{"type":"string"},"buy":{"type":"number","nullable":true},"sell":{"type":"number","nullable":true},"daysFrozen":{"type":"number"},"capped":{"type":"boolean","description":"la ventana entera está quieta, así que daysFrozen es un piso"},"lastChangedAt":{"type":"string","format":"date-time","nullable":true},"extreme":{"type":"string","nullable":true,"enum":["min-sell","max-sell","min-buy","max-buy"]}}}}}}}}}}}},"/health":{"get":{"tags":["Health"],"summary":"Verificar estado del servicio","description":"Endpoint de health check que verifica el estado del servicio\nbasándose en la última sincronización de datos y la conectividad\ncon la base de datos.\n\nEstados posibles:\n- **ok**: Sincronización reciente (≤15 minutos) y DB conectada\n- **degraded**: Sincronización ligeramente retrasada (15-30 min), DB desconectada, o datos de sync inválidos\n- **error**: Sincronización muy antigua (>30 minutos)\n\nEste endpoint solo retorna 500 cuando la sincronización tiene más de 30 minutos de antigüedad.\nLos estados \"degraded\" retornan 200 para evitar falsos positivos en health checks.\n","responses":{"200":{"description":"Servicio saludable o degradado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"},"examples":{"healthy":{"summary":"Servicio funcionando correctamente","value":{"status":"ok","message":"Sync is recent","timestamp":"2024-08-09T12:00:00.000Z","uptime":3600,"database":{"connected":true,"readyState":1,"readyStateText":"connected"},"sync":{"available":true,"lastSync":"2024-08-09T12:00:00.000Z","minutesAgo":5}}},"degraded":{"summary":"Servicio con degradación","value":{"status":"degraded","message":"Database is not connected","timestamp":"2024-08-09T12:00:00.000Z"}},"no_sync_file":{"summary":"Sin archivo de sincronización","value":{"status":"ok","message":"No sync file found - assuming healthy"}}}}}},"500":{"description":"Servicio con problemas graves (sync > 30 min)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"},"examples":{"unhealthy":{"summary":"Sincronización muy antigua","value":{"status":"error","message":"Sync is too old","sync":{"available":true,"lastSync":"2024-08-09T10:00:00.000Z","minutesAgo":120}}}}}}}}}},"/debug/sync":{"get":{"tags":["Debug"],"summary":"Estado detallado de la última sincronización","description":"Retorna información detallada sobre la última sincronización,\nincluyendo resultados por cada origen (casa de cambio),\ntiempos de ejecución y errores individuales.\n","responses":{"200":{"description":"Información de sincronización","content":{"application/json":{"schema":{"type":"object","properties":{"lastSync":{"type":"string","format":"date-time"},"minutesAgo":{"type":"number"},"syncResults":{"type":"object"},"syncFileExists":{"type":"boolean"}}}}}}}}},"/debug/db":{"get":{"tags":["Debug"],"summary":"Estado de la conexión a la base de datos","description":"Retorna información sobre la conexión a MongoDB,\nincluyendo estado de la conexión, nombre de la base de datos,\ny conteo de documentos en las colecciones principales.\n","responses":{"200":{"description":"Estado de la base de datos","content":{"application/json":{"schema":{"type":"object","properties":{"connection":{"type":"object"},"collections":{"type":"object"}}}}}}}}},"/debug/origins":{"get":{"tags":["Debug"],"summary":"Estado de los orígenes (casas de cambio)","description":"Retorna la lista de todos los orígenes configurados con\ninformación detallada de cada uno, incluyendo si tienen\ndatos actualizados para hoy.\n","responses":{"200":{"description":"Estado de los orígenes","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"origins":{"type":"array","items":{"type":"object"}}}}}}}}}},"/debug/config":{"get":{"tags":["Debug"],"summary":"Configuración del servidor (sin datos sensibles)","description":"Retorna información sobre la configuración del servidor,\nincluyendo versión de Node.js, tiempo de actividad,\nuso de memoria y zona horaria configurada.\n","responses":{"200":{"description":"Configuración del servidor","content":{"application/json":{"schema":{"type":"object","properties":{"nodeVersion":{"type":"string"},"uptime":{"type":"number"},"memory":{"type":"object"},"timezone":{"type":"string"}}}}}}}}},"/ai/insights":{"post":{"tags":["AI Insights"],"summary":"Obtener análisis de mercado con IA","description":"Genera análisis e insights del mercado de cambios uruguayo usando inteligencia artificial.\n\nTipos de análisis disponibles:\n- **market_summary**: Resumen general del estado del mercado\n- **currency_analysis**: Análisis detallado de una moneda específica\n- **best_rates**: Mejores oportunidades de compra/venta\n- **trend_analysis**: Análisis de tendencias históricas\n- **custom**: Pregunta personalizada sobre el mercado\n\nLas respuestas se generan en el idioma solicitado (es, en, pt) y se cachean por 10 minutos.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["market_summary","currency_analysis","best_rates","trend_analysis","custom"],"description":"Tipo de análisis a generar"},"language":{"type":"string","enum":["es","en","pt"],"default":"es","description":"Idioma de la respuesta"},"currency":{"type":"string","description":"Código de moneda para análisis específico (ej. USD, EUR)"},"origin":{"type":"string","description":"Casa de cambio para análisis específico"},"customPrompt":{"type":"string","description":"Pregunta personalizada (solo para type=custom)"}}},"examples":{"market_summary":{"summary":"Resumen del mercado","value":{"type":"market_summary","language":"es"}},"currency_analysis":{"summary":"Análisis de USD","value":{"type":"currency_analysis","language":"es","currency":"USD"}},"custom":{"summary":"Pregunta personalizada","value":{"type":"custom","language":"es","customPrompt":"¿Cuál es la mejor casa de cambio para comprar dólares hoy?"}}}}}},"responses":{"200":{"description":"Análisis generado exitosamente","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIInsightResponse"}}}},"400":{"description":"Parámetros inválidos"},"503":{"description":"Servicio de IA no configurado"}}}},"/ai/status":{"get":{"tags":["AI Insights"],"summary":"Estado del servicio de IA","description":"Verifica si el servicio de inteligencia artificial está configurado y disponible.","responses":{"200":{"description":"Estado del servicio de IA","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"boolean"},"model":{"type":"string"},"availableTypes":{"type":"array","items":{"type":"string"}},"supportedLanguages":{"type":"array","items":{"type":"string"}}}}}}}}}},"/cache/status":{"get":{"tags":["Health"],"summary":"Estado del cache Redis","description":"Retorna información sobre el estado de la conexión Redis y estadísticas.","responses":{"200":{"description":"Estado del cache","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"connected":{"type":"boolean"},"latencyMs":{"type":"number"}}}}}}}}},"/cache/flush":{"post":{"tags":["Health"],"summary":"Limpiar cache Redis","description":"Limpia todas las entradas del cache Redis.","responses":{"200":{"description":"Cache limpiado exitosamente","content":{"application/json":{"schema":{"type":"object","properties":{"flushed":{"type":"boolean"},"keysDeleted":{"type":"number"}}}}}}}}},"/exchange/{origin}":{"get":{"tags":["Exchange Data"],"summary":"Obtener tipo de cambio específico por casa de cambio","description":"Retorna los tipos de cambio de una casa de cambio específica.\nOpcionalmente se puede filtrar por código de moneda.\n\nPara obtener la lista de casas de cambio válidas, use `/parameters/origins`.\n","parameters":[{"$ref":"#/components/parameters/OriginParam"},{"$ref":"#/components/parameters/DateParam"}],"responses":{"200":{"description":"Tipos de cambio de la casa especificada","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeData"}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/exchange/{origin}/{code}":{"get":{"tags":["Exchange Data"],"summary":"Obtener tipo de cambio específico por casa y moneda","description":"Retorna el tipo de cambio de una moneda específica en una casa de cambio.\n\nPara obtener la lista de casas de cambio válidas, use `/parameters/origins`.\nPara obtener la lista de monedas válidas, use `/parameters/currencies`.\n","parameters":[{"$ref":"#/components/parameters/OriginParam"},{"$ref":"#/components/parameters/CurrencyCodeParam"},{"$ref":"#/components/parameters/DateParam"}],"responses":{"200":{"description":"Tipo de cambio específico encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeData"},"examples":{"found":{"summary":"Tipo de cambio encontrado","value":{"origin":"la_favorita","code":"USD","type":"","buy":38.75,"sell":41.15,"date":"2025-08-09T03:00:00.000Z","name":"Dólar Estadounidense"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"404":{"description":"Tipo de cambio no encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"origin":{"type":"string"},"code":{"type":"string"},"error":{"type":"string"}}},"example":{"origin":"la_favorita","code":"USD","error":"not found"}}}}}}},"/distances":{"get":{"tags":["Locations"],"summary":"Calcular distancias a casas de cambio","description":"Calcula las distancias desde una ubicación específica a todas las casas\nde cambio disponibles. Útil para encontrar las sucursales más cercanas.\n","parameters":[{"$ref":"#/components/parameters/LatitudeParam"},{"$ref":"#/components/parameters/LongitudeParam"}],"responses":{"200":{"description":"Lista de casas de cambio con distancias calculadas","content":{"application/json":{"schema":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Location"},{"type":"object","properties":{"distance":{"type":"number","description":"Distancia en metros"}}}]}},"examples":{"nearby_locations":{"summary":"Ubicaciones cercanas","value":[{"name":"La Favorita Centro","address":"Av. 18 de Julio 1234","latitude":-34.9173,"longitude":-56.1501,"department":"Montevideo","distance":1250.5},{"name":"Banco República Centro","address":"18 de Julio 1567","latitude":-34.9058,"longitude":-56.1909,"department":"Montevideo","distance":2100.8}]}}}}},"400":{"description":"Parámetros de ubicación faltantes o inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/geocoding":{"post":{"tags":["Geocoding"],"summary":"Geocodificar una dirección","description":"Convierte una dirección de texto en coordenadas geográficas.\nUtiliza el servicio OpenStreetMap Nominatim para la geocodificación.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string","description":"Dirección a geocodificar","example":"18 de Julio 1234, Montevideo, Uruguay"}},"required":["address"]}}}},"responses":{"200":{"description":"Resultados de geocodificación","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GeocodeResponse"}},"examples":{"found":{"summary":"Dirección encontrada","value":[{"display_name":"18 de Julio 1234, Montevideo, Uruguay","lat":"-34.9058","lon":"-56.1909","boundingbox":["-34.9158","-34.8958","-56.2009","-56.1809"]}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/bcu":{"get":{"tags":["BCU"],"summary":"Obtener datos del Banco Central del Uruguay","description":"Retorna los tipos de cambio oficiales del Banco Central del Uruguay (BCU).\nIncluye cotizaciones oficiales para diferentes monedas.\n","responses":{"200":{"description":"Datos del BCU obtenidos exitosamente","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeData"}},"examples":{"bcu_data":{"summary":"Datos oficiales del BCU","value":[{"origin":"bcu","code":"USD","type":"INTERBANCARIO","buy":40.25,"sell":40.75,"date":"2024-08-09T12:00:00.000Z","isInterBank":true}]}}}}}}}},"/localData":{"get":{"tags":["Locations"],"summary":"Obtener datos locales de casas de cambio","description":"Retorna información detallada sobre las casas de cambio,\nincluyendo ubicaciones, departamentos y datos de contacto.\n","responses":{"200":{"description":"Datos locales obtenidos exitosamente","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{"type":"string","description":"Nombre de la casa de cambio"},"website":{"type":"string","description":"Sitio web oficial"},"departments":{"type":"array","items":{"type":"string"},"description":"Departamentos donde opera"}}}},"examples":{"local_data":{"summary":"Información de casas de cambio","value":{"la_favorita":{"name":"La Favorita","website":"https://www.lafavorita.com.uy","departments":["Montevideo","Canelones","Maldonado"]},"brou":{"name":"Banco República","website":"https://www.brou.com.uy","departments":["Montevideo","Canelones","San José"]}}}}}}}}}},"/aduana":{"get":{"tags":["Aduana"],"summary":"Guía de problemas con la aduana uruguaya (normas, procedimientos y testimonios citados)","description":"Datos que alimentan /problemas-con-la-aduana-uruguay: doce problemas típicos de compras\nimportadas y encomiendas (paquete retenido, factura exigida, franquicia agotada, etc.),\ncada uno con la norma aplicable, los pasos a seguir y testimonios reales citados de\nr/uruguay. Se sincroniza una vez por semana (pm2 `currency-aduana`, lunes 09:30 UTC):\nse cosechan hilos nuevos de Reddit, se etiquetan con IA y se re-chequean los hechos\nlegales contra la norma oficial (impo.com.uy / gub.uy) con un modelo con acceso a\nbúsqueda — la IA solo puede señalar un cambio de ley para revisión humana, nunca\npublicarlo directamente.\n\nCada hecho (`facts[]`) trae su fuente (`sourceId`, resoluble en `sources[]`) y dos\nfechas que NO son intercambiables: `verifiedAt` es exclusivamente humana — significa que\nuna persona abrió el decreto y leyó el número — mientras que `aiCheckedAt` es lo que\nescribe el re-chequeo semanal cuando un modelo con acceso a búsqueda confirmó el mismo\nvalor leyendo la norma; es una señal de frescura, no de verificación humana. La página\ndebe mostrarlas por separado (\"verificado contra la norma\" vs. \"último control\nautomático\"), nunca fusionadas.\n\n`stale` es `true` cuando el job semanal no corrió en más de dos semanas (o nunca corrió).\nUn problema sin testimonios de Reddit igual se publica: la norma y el procedimiento son\nel contenido, los testimonios son corroboración.\n","responses":{"200":{"description":"Hechos legales, problemas con procedimiento y testimonios citados","content":{"application/json":{"schema":{"type":"object","properties":{"facts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"franquicia.tope_anual_usd"},"label":{"type":"string"},"value":{"oneOf":[{"type":"number"},{"type":"string"}]},"unit":{"type":"string","nullable":true,"example":"USD"},"sourceId":{"type":"string"},"article":{"type":"string","nullable":true},"verifiedAt":{"type":"string","description":"Fecha ISO — escrita SOLO por un humano que leyó la norma."},"aiCheckedAt":{"type":"string","nullable":true,"description":"Fecha ISO — escrita SOLO por el re-chequeo semanal de la IA."},"origin":{"type":"string","enum":["baseline","ai"]}}}},"problems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"retenido"},"title":{"type":"string"},"symptom":{"type":"string"},"norm":{"type":"string"},"sourceIds":{"type":"array","items":{"type":"string"}},"steps":{"type":"array","items":{"type":"string"}},"deadline":{"type":"string","nullable":true},"claimTemplate":{"type":"string","nullable":true},"verified":{"type":"boolean"},"quotes":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"author":{"type":"string"},"date":{"type":"string"},"score":{"type":"number"},"permalink":{"type":"string"}}}},"reports":{"type":"number","example":12}}}},"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"norm":{"type":"string"},"url":{"type":"string"},"checkedAt":{"type":"string"}}}},"updatedAt":{"type":"string","nullable":true},"stale":{"type":"boolean","description":"true cuando el job semanal no corrió en más de dos semanas"}}}}}}}}},"/banks-news":{"get":{"tags":["Banks"],"summary":"Novedades y análisis del sector bancario uruguayo (búsqueda con grounding, citada)","description":"Datos que alimentan la sección de novedades de /mejores-bancos-uruguay: una búsqueda con\ngrounding (Gemini + Google Search) por banco/fintech uruguayo, más un análisis de sector\nsintetizado SOLO a partir de lo que esa búsqueda encontró. Se sincroniza una vez por día\n(pm2 `currency-banks-news`).\n\n`lang` acepta `es` (default), `en` o `pt`; cualquier otro valor cae a `es`.\n\n`unavailable: true` significa que el briefing para ese idioma todavía no se generó nunca\n(por ejemplo, si el job nunca corrió con GEMINI_API_KEY configurada) — no que la búsqueda\nhaya fallado esta vez, en cuyo caso se sigue sirviendo el último briefing bueno guardado.\n\n`headlines[].link` es la URL REAL de la fuente (resuelta a partir del redirect de Google),\nnunca el wrapper `vertexaisearch.cloud.google.com/grounding-api-redirect/...`.\n","parameters":[{"in":"query","name":"lang","schema":{"type":"string","enum":["es","en","pt"],"default":"es"},"description":"Idioma del análisis y los resúmenes. Cualquier otro valor cae a \"es\"."}],"responses":{"200":{"description":"Novedades por entidad + análisis de sector","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"itau"},"name":{"type":"string","example":"Itaú"},"insight":{"type":"string","nullable":true},"headlines":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"source":{"type":"string","example":"elpais.com.uy"},"link":{"type":"string","description":"URL real de la fuente, nunca el redirect de Google."}}}}}}},"analysis":{"type":"string","nullable":true,"description":"Análisis de sector en Markdown, sintetizado solo de los items."},"asOf":{"type":"string"},"unavailable":{"type":"boolean","description":"true cuando este idioma todavía no tiene un briefing generado."}}}}}}}}},"/uy-figures":{"get":{"tags":["Indicators"],"summary":"Indicadores clave de Uruguay (salario mínimo, BPC, boleto, inflación)","description":"Datos que alimentan /salud-financiera, /indicadores y /herramientas/costo-de-vida:\nsalario mínimo nacional, BPC, boleto común de Montevideo (STM) e inflación anual (IPC),\nvía una búsqueda con grounding (Gemini + Google Search). Se sincroniza una vez por día\n(pm2 `currency-figures`).\n\nCada valor debe caer dentro de una banda de plausibilidad para ser aceptado; un valor\nfuera de banda se descarta y se mantiene el último dato bueno (o el baseline verificado).\n`updated[]` lista qué campos vinieron de una búsqueda en vivo en este ciclo — todo lo\ndemás es el baseline verificado. `asOf: null` significa que todavía no se generó ningún\ndato en vivo (nunca corrió el job con GEMINI_API_KEY configurada).\n","responses":{"200":{"description":"Indicadores clave de Uruguay","content":{"application/json":{"schema":{"type":"object","properties":{"salarioMinimo":{"type":"number","description":"UYU/mes, nominal"},"bpc":{"type":"number","description":"Base de Prestaciones y Contribuciones, UYU"},"boletoStm":{"type":"number","description":"Boleto común de Montevideo con STM, UYU"},"inflacionAnual":{"type":"number","description":"Variación anual del IPC, %"},"asOf":{"type":"string","nullable":true},"updated":{"type":"array","items":{"type":"string"},"description":"Campos actualizados en vivo en este ciclo."},"sources":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":"string"}}}}}}}}}}}},"/site-analytics-realtime":{"get":{"tags":["Indicators"],"summary":"Actividad en vivo del sitio (últimos 30 minutos, GA4 Realtime)","description":"Lo único de /estadisticas-del-sitio que no puede salir de la foto diaria: cuánta gente hay\nahora, cómo se movió minuto a minuto en la última media hora y qué páginas están abiertas.\nFuente: Google Analytics 4 Realtime API.\n\nSólo agregados: un conteo, una forma por minuto y TÍTULOS de página (la API en tiempo real\nno expone rutas, así que ni siquiera existe la posibilidad de filtrar un query string).\n`activeUsers` son usuarios ÚNICOS de los últimos 30 minutos: no es la suma de `perMinute`,\ndonde la misma persona aparece en cada minuto en que estuvo activa.\n\nCacheado 45 s en Redis (compartido por las dos instancias del cluster). Si el servidor no\ntiene GA4 configurado responde todo en cero — nunca un 500.\n","responses":{"200":{"description":"Actividad de los últimos 30 minutos","content":{"application/json":{"schema":{"type":"object","properties":{"asOf":{"type":"string","format":"date-time"},"activeUsers":{"type":"number","description":"Usuarios únicos, últimos 30 min"},"activeUsersLast5":{"type":"number","description":"Pico por minuto en los últimos 5 min"},"perMinute":{"type":"array","description":"30 entradas, de la más vieja a la más nueva.","items":{"type":"object","properties":{"minutesAgo":{"type":"number"},"activeUsers":{"type":"number"}}}},"pages":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"activeUsers":{"type":"number"},"views":{"type":"number"}}}}}}}}}}}},"/cost-of-living":{"get":{"tags":["Indicators"],"summary":"Figuras en vivo para el costo de vida en Uruguay (salario, boleto, alquileres)","description":"Datos que alimentan /herramientas/costo-de-vida: salario mínimo, boleto común de\nMontevideo (STM) y alquileres típicos (monoambiente, 1 y 2 dormitorios) vía una búsqueda\ncon grounding (Gemini + Google Search). Se sincroniza una vez por día (pm2\n`currency-costs`).\n\nDevuelve SOLO las cinco figuras validadas — la app aplica esos valores sobre su propio\nmodelo de costos (COST_MODEL), que no vive acá para no duplicar esa tabla. Cada figura\ndebe caer dentro de una banda de plausibilidad para ser aceptada; una fuera de banda\nsimplemente no aparece en `figures` ni en `updated`. `asOf: null` significa que todavía\nno se generó ningún dato en vivo.\n","responses":{"200":{"description":"Figuras en vivo de costo de vida","content":{"application/json":{"schema":{"type":"object","properties":{"figures":{"type":"object","properties":{"salarioMinimo":{"type":"number","nullable":true},"boletoStm":{"type":"number","nullable":true},"rentMono":{"type":"number","nullable":true},"rent1":{"type":"number","nullable":true},"rent2":{"type":"number","nullable":true}}},"asOf":{"type":"string","nullable":true},"updated":{"type":"array","items":{"type":"string"}},"sources":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":"string"}}}}}}}}}}}},"/temas-analysis":{"get":{"tags":["Indicators"],"summary":"Lectura trimestral por IA de los temas de dinero más consultados en Uruguay","description":"Análisis generado con Gemini (grounded) sobre el ranking de temas de dinero de Reddit, regenerado cada 90 días. Solo lee el último snapshot almacenado; nunca gasta una llamada de IA. Devuelve `{ overview: string[], topics: {id,trend,insight}[], sources, asOf }`.\n","responses":{"200":{"description":"Análisis almacenado (o vacío si aún no se generó)."}}}},"/debt-relief":{"get":{"tags":["Indicators"],"summary":"Topes de usura vigentes del BCU (crédito al consumo)","description":"Datos que alimentan /saldar-deudas-uruguay: los topes de usura que publica el Banco\nCentral del Uruguay (Ley 18.212) para crédito al consumo, con y sin autorización de\ndescuento, tramo menor a 10.000 UI — vía una búsqueda con grounding (Gemini + Google\nSearch). Se sincroniza una vez por mes (pm2 `currency-debt-relief`, día 1).\n\nDevuelve SOLO los topes (`usuryCaps`, índice 0 = con descuento, 1 = sin) — `refiRates` y\n`period` son contenido estático de la página y no viven acá. Cada valor debe caer dentro\nde una banda de plausibilidad para ser aceptado; si nada pasa la banda se sirve el\núltimo baseline verificado. `asOf: null` significa que todavía no se generó ningún dato\nen vivo.\n","responses":{"200":{"description":"Topes de usura vigentes","content":{"application/json":{"schema":{"type":"object","properties":{"usuryCaps":{"type":"array","items":{"type":"object","properties":{"segmento":{"type":"string"},"tasaMedia":{"type":"number"},"topeTasa":{"type":"number"},"topeMora":{"type":"number"}}}},"asOf":{"type":"string","nullable":true},"updated":{"type":"array","items":{"type":"string"}},"sources":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":"string"}}}}}}}}}}}},"/bcu-rates":{"get":{"tags":["Indicators"],"summary":"Tasas medias y topes de usura del BCU (Ley 18.212) para crédito al consumo","description":"La grilla que publica el BCU cada mes (ventana trimestral móvil) para\nFamilias > Consumo > SIN autorización de descuento, capital menor a 2:000.000 UI.\nSeis filas: pesos y dólares, por tramo de 10.000 UI y por plazo.\n\nCada fila trae la tasa media publicada y los dos topes. Los topes se validan contra\nla aritmética de la Ley 18.212 (media x 1,55 y x 1,80) antes de almacenarse, así que\nuna lectura mal hecha nunca llega a servirse: se conserva la anterior.\n\nSe refresca a diario (pm2 `currency-bcu-rates`) porque la tabla entra en vigencia el\ndía 1 y el comunicado aparece en un día impredecible del mes anterior.\n","responses":{"200":{"description":"Tabla vigente","content":{"application/json":{"schema":{"type":"object","properties":{"periodo":{"type":"string","example":"abril-junio de 2026"},"vigenteDesde":{"type":"string","example":"2026-08-01"},"asOf":{"type":"string","nullable":true},"rows":{"type":"array","items":{"type":"object","properties":{"bracket":{"type":"string","enum":["menor10kUI","mayor10kUI"]},"cortoPlazo":{"type":"boolean"},"currency":{"type":"string","enum":["UYU","USD"]},"media":{"type":"number","example":84.47},"tope":{"type":"number","example":130.9285},"topeMora":{"type":"number","example":152.046}}}},"sources":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":"string"}}}}}}}}}}}},"/loan-rates":{"get":{"tags":["Indicators"],"summary":"Tasas efectivas anuales (TEA) publicadas por prestamistas uruguayos","description":"Datos que alimentan \\prestamos-uruguay: la TEA representativa publicada por cada\nprestamista (bancos, financieras, cooperativas, fintech), obtenida por un parser regex\nsobre la propia página del prestamista y, cuando no hay parser o falla, por una búsqueda\ncon grounding (Gemini + Google Search) que exige que la cita resuelva al dominio propio\ndel prestamista. Se sincroniza una vez por día (pm2 `currency-loans`).\n\n`rates` trae solo el último valor conocido por prestamista; `history` guarda una entrada\npor prestamista y por día — una serie temporal que nunca se trunca ni se regenera. Un\nscrape fallido o implausible nunca borra el último valor bueno.\n","responses":{"200":{"description":"TEAs vigentes por prestamista","content":{"application/json":{"schema":{"type":"object","properties":{"rates":{"type":"object","description":"Por id de prestamista.","additionalProperties":{"type":"object","properties":{"teaPct":{"type":"number"},"scrapedAt":{"type":"string"}}}},"history":{"type":"object","description":"Por id de prestamista, un array con una entrada por día.","additionalProperties":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"teaPct":{"type":"number"},"source":{"type":"string"},"method":{"type":"string","enum":["regex","gemini"]}}}}},"updatedAt":{"type":"string"}}}}}}}}},"/preferential-rates":{"get":{"tags":["Exchange Data"],"summary":"Cotizaciones preferenciales por proveedor y monto","description":"Contrato común para bancos, fintech y plataformas que publican distintas\ncotizaciones según el monto. Devuelve la captura vigente y una captura por\ndía calendario de Uruguay. Actualmente incluye Santander; nuevos proveedores\nse agregan mediante adaptadores sin cambiar el contrato.\n\n`minAmount` es inclusivo, `maxAmount` es exclusivo y `null` significa que\nno existe límite superior. Al enviar `currency` y `amount`, cada captura\nincluye `selectedRate` con la franja aplicable. Las credenciales y sesiones\nusadas para consultar fuentes autenticadas nunca forman parte de la respuesta.\n","parameters":[{"name":"provider","in":"query","required":false,"description":"Limita el resultado a un proveedor registrado.","schema":{"type":"string","enum":["santander"],"example":"santander"}},{"name":"currency","in":"query","required":false,"description":"Código ISO 4217 de tres letras.","schema":{"type":"string","pattern":"^[A-Za-z]{3}$","example":"USD"}},{"name":"amount","in":"query","required":false,"description":"Monto en la moneda consultada. Requiere enviar currency.","schema":{"type":"number","minimum":0,"example":5000}}],"responses":{"200":{"description":"Catálogo de tasas por monto y su histórico diario","content":{"application/json":{"schema":{"type":"object","properties":{"currency":{"type":"string","nullable":true,"example":"USD"},"amount":{"type":"number","nullable":true,"example":5000},"providerCount":{"type":"integer","example":1},"updatedAt":{"type":"string","format":"date-time"},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","example":"santander"},"displayName":{"type":"string","example":"Santander"},"source":{"type":"string","format":"uri"},"requiresAuthentication":{"type":"boolean","example":true},"currencies":{"type":"array","items":{"type":"string"}},"boundaryRule":{"type":"string"},"current":{"type":"object","nullable":true,"properties":{"date":{"type":"string","format":"date"},"scrapedAt":{"type":"string","format":"date-time"},"rates":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","example":"USD"},"buy":{"type":"number","example":39.4},"sell":{"type":"number","example":40.95},"minAmount":{"type":"number","example":1001},"maxAmount":{"type":"number","nullable":true,"example":10000}}}},"selectedRate":{"type":"object","nullable":true}}},"history":{"type":"array","items":{"type":"object"}},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}},"400":{"description":"Proveedor, moneda o monto inválido"}}}},"/santander/preferential-rates":{"get":{"tags":["Exchange Data"],"summary":"Cotizaciones preferenciales de Santander por monto","description":"Devuelve las franjas de compra y venta que Santander muestra dentro de Supernet,\njunto con una fotografía por día calendario de Uruguay. La sincronización normal\nde cotizaciones actualiza la entrada del día sin duplicarla; una falla nunca borra\nel último dato válido.\n\n`minAmount` es inclusivo, `maxAmount` es exclusivo y `null` representa una franja\nsin límite superior. Si se envía `amount`, el endpoint agrega `selectedRate` para\nese monto en la cotización vigente y en cada punto histórico. Cuando `amount` se\nenvía sin `currency`, se usa USD.\n","parameters":[{"name":"currency","in":"query","required":false,"description":"Código ISO de tres letras. Si se omite, se devuelven todas las monedas.","schema":{"type":"string","pattern":"^[A-Za-z]{3}$","example":"USD"}},{"name":"amount","in":"query","required":false,"description":"Monto en la moneda consultada usado para elegir la franja.","schema":{"type":"number","minimum":0,"example":5000}}],"responses":{"200":{"description":"Tasas vigentes e histórico diario","content":{"application/json":{"schema":{"type":"object","properties":{"bank":{"type":"string","example":"santander"},"source":{"type":"string"},"currency":{"type":"string","nullable":true},"amount":{"type":"number","nullable":true},"currencies":{"type":"array","items":{"type":"string"}},"boundaryRule":{"type":"string"},"current":{"type":"object","nullable":true},"history":{"type":"array","items":{"type":"object"}},"updatedAt":{"type":"string"}}}}}},"400":{"description":"Moneda o monto inválido"}}}},"/fortex":{"get":{"tags":["Exchange Data"],"summary":"Obtener datos de Fortex","description":"Retorna los tipos de cambio de Fortex para el día actual.\nFortex es una plataforma de intercambio de divisas.\n","responses":{"200":{"description":"Datos de Fortex obtenidos exitosamente","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeData"}}}}}}}},"/position_stack":{"get":{"tags":["Geocoding"],"summary":"Geocodificación inversa con PositionStack","description":"Convierte coordenadas geográficas en información de ubicación\nutilizando el servicio PositionStack.\n","parameters":[{"name":"query","in":"query","required":true,"description":"Coordenadas en formato \"latitud,longitud\"","schema":{"type":"string","pattern":"^-?\\d+\\.?\\d*,-?\\d+\\.?\\d*$"},"example":"-34.9173,-56.1501"},{"name":"limit","in":"query","required":false,"description":"Número máximo de resultados","schema":{"type":"integer","minimum":1,"maximum":100,"default":1}}],"responses":{"200":{"description":"Información de ubicación obtenida","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"latitude":{"type":"number"},"longitude":{"type":"number"},"label":{"type":"string"},"name":{"type":"string"},"country":{"type":"string"},"region":{"type":"string"}}}}}}}}}}}},"/exchanges/{origin}":{"get":{"tags":["Locations"],"summary":"Obtener sucursales de una casa de cambio","description":"Retorna las sucursales de una casa de cambio específica.\nOpcionalmente filtrado por ubicación.\n\nPara obtener la lista de casas de cambio válidas, use `/parameters/origins`.\n","parameters":[{"$ref":"#/components/parameters/OriginParam"},{"$ref":"#/components/parameters/LatitudeParam"},{"$ref":"#/components/parameters/LongitudeParam"}],"responses":{"200":{"description":"Lista de sucursales","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Location"}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/exchanges/{origin}/{location}":{"get":{"tags":["Locations"],"summary":"Obtener sucursales por casa de cambio y ubicación","description":"Retorna las sucursales de una casa de cambio en una ubicación específica.\n\nPara obtener la lista de casas de cambio válidas, use `/parameters/origins`.\nPara obtener la lista de ubicaciones válidas, use `/parameters/locations`.\n","parameters":[{"$ref":"#/components/parameters/OriginParam"},{"$ref":"#/components/parameters/LocationParam"},{"$ref":"#/components/parameters/LatitudeParam"},{"$ref":"#/components/parameters/LongitudeParam"}],"responses":{"200":{"description":"Lista de sucursales filtrada por ubicación","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Location"}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/bcu/{origin}":{"get":{"tags":["BCU"],"summary":"Obtener detalles BCU por casa de cambio","description":"Retorna información detallada del BCU para una casa de cambio específica.\n\nPara obtener la lista de casas de cambio válidas, use `/parameters/origins`.\n","parameters":[{"$ref":"#/components/parameters/OriginParam"}],"responses":{"200":{"description":"Detalles BCU obtenidos exitosamente","content":{"application/json":{"schema":{"type":"object","properties":{"origin":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeData"}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/evolution/{origin}/{code}":{"get":{"tags":["Evolution"],"summary":"Obtener evolución histórica de una moneda","description":"Retorna datos históricos de evolución de precios para una moneda específica\nen una casa de cambio durante un período determinado.\n\nPara obtener la lista de casas de cambio válidas, use `/parameters/origins`.\nPara obtener la lista de monedas válidas, use `/parameters/currencies`.\n","parameters":[{"$ref":"#/components/parameters/OriginParam"},{"$ref":"#/components/parameters/CurrencyCodeParam"},{"$ref":"#/components/parameters/PeriodParam"}],"responses":{"200":{"description":"Datos de evolución histórica","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyEvolution"}},"examples":{"evolution_data":{"summary":"Evolución del USD en La Favorita","value":[{"date":"2025-08-01","buy":38.75,"sell":41.15,"avg":39.95},{"date":"2025-08-02","buy":38.8,"sell":41.2,"avg":40}]}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/evolution/{origin}/{code}/{type}":{"get":{"tags":["Evolution"],"summary":"Obtener evolución histórica por tipo de cambio","description":"Retorna datos históricos de evolución de precios para un tipo específico\nde cambio (BILLETE, CABLE, etc.) de una moneda en una casa de cambio.\n\nPara obtener la lista de casas de cambio válidas, use `/parameters/origins`.\nPara obtener la lista de monedas válidas, use `/parameters/currencies`.\nPara obtener la lista de tipos válidos, use `/parameters/types`.\n","parameters":[{"$ref":"#/components/parameters/OriginParam"},{"$ref":"#/components/parameters/CurrencyCodeParam"},{"$ref":"#/components/parameters/ExchangeTypeParam"},{"$ref":"#/components/parameters/PeriodParam"}],"responses":{"200":{"description":"Datos de evolución histórica por tipo","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyEvolution"}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/parameters/origins":{"get":{"tags":["Parameters"],"summary":"Obtener todas las casas de cambio disponibles","description":"Retorna la lista completa de todas las casas de cambio (origins) disponibles\nen el sistema. Útil para validar parámetros y construir interfaces de usuario.\n","responses":{"200":{"description":"Lista de casas de cambio disponibles","content":{"application/json":{"schema":{"type":"object","properties":{"origins":{"type":"array","items":{"type":"string"},"description":"Lista de nombres de casas de cambio"},"count":{"type":"integer","description":"Número total de casas de cambio"}}},"examples":{"success":{"summary":"Casas de cambio disponibles","value":{"origins":["la_favorita","cambio_minas","brou","cambio_regul","itau","oca","prex","santander","bcu","cambilex"],"count":45}}}}}}}}},"/parameters/currencies":{"get":{"tags":["Parameters"],"summary":"Obtener todas las monedas disponibles","description":"Retorna la lista de todas las monedas (currency codes) disponibles\nen el sistema, obtenidas de los datos actuales.\n","parameters":[{"$ref":"#/components/parameters/DateParam"}],"responses":{"200":{"description":"Lista de monedas disponibles","content":{"application/json":{"schema":{"type":"object","properties":{"currencies":{"type":"array","items":{"type":"string"},"description":"Lista de códigos de moneda (ISO 4217)"},"count":{"type":"integer","description":"Número total de monedas"}}},"examples":{"success":{"summary":"Monedas disponibles","value":{"currencies":["USD","EUR","ARS","BRL"],"count":4}}}}}}}}},"/parameters/types":{"get":{"tags":["Parameters"],"summary":"Obtener todos los tipos de cambio disponibles","description":"Retorna la lista de todos los tipos de cambio disponibles\n(BILLETE, CABLE, TRANSFERENCIA, etc.) en el sistema.\n","parameters":[{"$ref":"#/components/parameters/DateParam"},{"name":"origin","in":"query","description":"Filtrar por casa de cambio específica","required":false,"schema":{"type":"string"}},{"name":"code","in":"query","description":"Filtrar por moneda específica","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de tipos de cambio disponibles","content":{"application/json":{"schema":{"type":"object","properties":{"types":{"type":"array","items":{"type":"string"},"description":"Lista de tipos de cambio"},"count":{"type":"integer","description":"Número total de tipos"}}},"examples":{"success":{"summary":"Tipos disponibles","value":{"types":["BILLETE","CABLE","TRANSFERENCIA"],"count":3}}}}}}}}},"/parameters/locations":{"get":{"tags":["Parameters"],"summary":"Obtener todas las ubicaciones disponibles","description":"Retorna la lista de todas las ubicaciones/departamentos disponibles\npara buscar sucursales de casas de cambio.\n","parameters":[{"name":"origin","in":"query","description":"Filtrar por casa de cambio específica","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de ubicaciones disponibles","content":{"application/json":{"schema":{"type":"object","properties":{"locations":{"type":"array","items":{"type":"string"},"description":"Lista de departamentos/ubicaciones"},"count":{"type":"integer","description":"Número total de ubicaciones"}}},"examples":{"success":{"summary":"Ubicaciones disponibles","value":{"locations":["Montevideo","Canelones","Maldonado"],"count":3}}}}}}}}},"/parameters/all":{"get":{"tags":["Parameters"],"summary":"Obtener todos los parámetros disponibles","description":"Retorna un objeto consolidado con todos los parámetros válidos\ndel sistema: origins, currencies, types y locations.\n","parameters":[{"$ref":"#/components/parameters/DateParam"}],"responses":{"200":{"description":"Todos los parámetros disponibles","content":{"application/json":{"schema":{"type":"object","properties":{"origins":{"type":"array","items":{"type":"string"}},"currencies":{"type":"array","items":{"type":"string"}},"types":{"type":"array","items":{"type":"string"}},"locations":{"type":"array","items":{"type":"string"}},"counts":{"type":"object","properties":{"origins":{"type":"integer"},"currencies":{"type":"integer"},"types":{"type":"integer"},"locations":{"type":"integer"}}}}},"examples":{"success":{"summary":"Todos los parámetros","value":{"origins":["la_favorita","brou","cambio_minas","itau","oca","prex","santander"],"currencies":["USD","EUR","ARS","BRL","XAU","UI"],"types":["","BILLETE","CABLE","INTERBANCARIO"],"locations":["Montevideo","Canelones","Maldonado"],"counts":{"origins":42,"currencies":18,"types":8,"locations":19}}}}}}}}}}}}