El modelo de evidencia
Esta es la página que explica qué hace distinto a este servicio: no sirve números, sirve observaciones — con su evidencia, su historia y una línea de tiempo consultable.
El problema
Un tipo de cambio alimenta asientos contables, facturas y declaraciones. Años después, un auditor pregunta: ¿de dónde salió este número? Si la API que lo sirvió solo entregaba el valor del día, no hay respuesta: el dato pudo cambiar, la fuente no expone histórico de sus correcciones y el rastro se perdió. Este servicio está construido para que esa pregunta siempre tenga respuesta.
La observación: inmutable por diseño
Cada vez que el servicio observa una fuente oficial y obtiene un valor
nuevo, crea una observación: qué valor publicó la fuente
(compra/venta, canónico y crudo), para qué fecha
(fecha_serie), de qué fuente y con qué consulta exacta
(identidad_consulta), en qué instante
(capturado_en), con qué versión del parser, y con qué bytes
crudos de respaldo (evidencia_id y el fragmento
exacto del payload del que se parseó el valor).
Ninguno de esos campos se edita jamás. Si la fuente
corrige un valor, el servicio no reescribe la observación: crea una nueva
que la supersede y lo registra como evento. La observación
corregida queda consultable para siempre en
/v1/observations/{id}, con su cadena completa de ancestros y
sucesoras. Un dato que usó en 2026 sigue explicable en 2036, aunque haya
sido corregido en el camino.
La revisión: la línea de tiempo del dataset
Toda escritura al registro — una captura nueva, una recaptura idéntica, una corrección — avanza una revisión global del dataset. La revisión N es el estado del registro tras el commit número N: una línea de tiempo totalmente ordenada.
El parámetro as_of_revision de /v1/rates
consulta el dataset como era a esa revisión. La respuesta es
inmutable: puede pedirse hoy o dentro de diez años y son los mismos bytes
(por eso viaja con Cache-Control: immutable y
ETag).
Ejemplo: un auditor en 2028 reconstruye el 21 de agosto de 2026
Supongamos que en 2028 hay que demostrar qué se sabía del tipo de cambio del 21/08/2026 al terminar ese día. Paso 1: mapear el instante a una revisión.
curl -s -H "Authorization: Bearer $EKIA_TOKEN" \
'https://api.eurekia.pe/v1/revisions?at=2026-08-21T23:59:59-05:00'
{
"revision": 658,
"ts_commit": "2026-08-22T03:17:11.877559Z",
"dataset_revision": 711,
"request_id": "57d47928-1818-4157-b075-5ea23454a4d8",
"schema_version": "1.0"
}
Al cierre del 21/08/2026 en Lima regía la revisión 658. Paso 2: consultar las tasas ancladas a esa revisión.
curl -s -H "Authorization: Bearer $EKIA_TOKEN" \
'https://api.eurekia.pe/v1/rates?currency=USD&series=publicado&from=2026-08-21&to=2026-08-26&as_of_revision=658'
{
"items": [
{
"fecha_serie": "2026-08-21",
"compra": "3.351",
"venta": "3.361",
"fuente": "sunat_txt",
"cotejos": [],
"tiene_discrepancia_abierta": false,
"marcas": ["capturada", "recaptura_identica"],
"observacion_id": "f39403ae-c387-4045-8091-c901973f8feb",
"capturado_en": "2026-08-21T17:06:47.880642Z",
"superseded": false,
"superseded_por": null
}
],
"total": 1,
"next_page_token": null,
"dataset_revision": 658,
"request_id": "3720caed-3e07-4246-ae1e-3e28f8ecb094",
"schema_version": "1.0"
}
A la revisión 658 el registro conocía una fecha de ese rango (el 21/08); las capturas de los días siguientes ocurrieron en revisiones posteriores — exactamente lo que se sabía entonces, ni más ni menos. Dos detalles del ítem histórico:
supersededesfalseporque, a la revisión consultada, ese era el valor vigente.superseded_pormira el presente: si hoy ya existiera una corrección de esa observación, aquí aparecería su UUID. El auditor ve a la vez qué se sabía entonces y si cambió después.
La evidencia: verifique el hash usted mismo
Cada captura guarda los bytes exactos que la fuente
respondió (el body HTTP descomprimido, tal cual llegó) y su SHA-256.
/v1/evidence/{id} los devuelve. El siguiente ejemplo es real y
ejecutable: descarga la evidencia de la observación del 21/08/2026 y
verifica su hash. Fue ejecutado tal cual el 26/08/2026; las salidas son las
reales.
curl -s -H "Authorization: Bearer $EKIA_TOKEN" \
-D encabezados.txt -o evidencia.txt \
https://api.eurekia.pe/v1/evidence/f4a7a1c6-e1a4-4999-b175-7191e90c77c6
cat evidencia.txt
# 21/08/2026|3.351|3.361|
sha256sum evidencia.txt
# 40dad22e26fe688bd700781111a10212e3725ae1bf9c14df92de36787f3d5a59 evidencia.txt
grep -i x-sha256 encabezados.txt
# x-sha256: 40dad22e26fe688bd700781111a10212e3725ae1bf9c14df92de36787f3d5a59
Los 24 bytes descargados son el tipoCambio.txt que SUNAT
publicó ese día, y el hash calculado en su máquina coincide con el que el
servicio registró al capturarlo. La observación
(/v1/observations/f39403ae-…) referencia esta evidencia y el
fragmento exacto del que se parseó el valor: la cadena
valor → observación → bytes de la fuente queda cerrada.
Los cotejos: fuentes que se vigilan entre sí
Cuando existen observaciones de más de una fuente para la misma fecha,
el servicio las coteja y publica el resultado
(ok o discrepante) en el campo
cotejos de cada ítem. Ante una discrepancia, el servicio
no arbitra cuál fuente manda: la expone
(tiene_discrepancia_abierta: true) y deja la decisión al
consumidor.
Los límites, declarados
La honestidad del modelo incluye decir qué no prueba:
- Las fuentes de tipo de cambio comparten cadena de origen (la tabla de la SBS). El cotejo detecta errores de transmisión, parseo o publicación — no un error aguas arriba, común a ambas fuentes.
- "Inmutable" significa a prueba de la aplicación y de la operación normal (privilegios de solo-inserción en la base de datos). La evidencia forense fuerte son las copias de seguridad externas versionadas.
- Una corrección de la fuente posterior a la última consulta del día solo se detecta de forma indirecta (por cotejo posterior): la fuente no expone su propio histórico.
Qué guardar en su sistema
Dos campos por cada dato que tome: observacion_id y
dataset_revision. Con ellos, cualquier número en su sistema se
reconstruye años después: qué observación lo respaldaba, qué evidencia
tenía y qué contenía el dataset cuando lo tomó.