# La bitácora

> Cada llamada deja exactamente una fila inmutable: auditoría, correlación con soporte y fuente de verdad de la facturación.



Toda ejecución de una tool, entre por REST, por MCP, desde el dashboard o disparada por el scheduler, escribe exactamente **una** fila en la bitácora. La tabla es append-only por doble candado (ningún rol de aplicación tiene UPDATE ni DELETE, y no existe política que los permita), así que una fila escrita no se edita nunca. Sobre ese registro se apoyan tres cosas a la vez: la auditoría, el soporte y la facturación. No hay un contador de cobro separado que pueda divergir de lo que de verdad pasó.

## La anatomía de una fila [#la-anatomía-de-una-fila]

Las columnas que vas a usar, de la tabla real `bitacora_execution`:

| Columna                                          | Qué guarda                                                           | Por qué importa                                                                                     |
| ------------------------------------------------ | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `request_id`                                     | El `req_…` de esta llamada, generado por el servidor                 | La llave de correlación: viaja en el `meta` de cada respuesta y dentro de cada error                |
| `tool_id` / `connector_code`                     | Qué se ejecutó (`sii.rcv.consultar`) y de qué sistema                | El grano de todo análisis de uso                                                                    |
| `connection_id`                                  | La conexión (`conn_…`); null en conectores sin conexión              | Atribuye la llamada a una empresa concreta                                                          |
| `plane`                                          | `action` o `read`                                                    | Distingue una consulta barata de una sincronización real ([Los dos planos](/docs/conceptos/planos)) |
| `surface`                                        | `rest`, `mcp_meta`, `mcp_native`, `dashboard` o `hosted`             | Por dónde entró la llamada                                                                          |
| `actor_id` / `actor_type`                        | Quién actuó: `user`, `agent`, `system` o `connect_session`           | La atribución (abajo)                                                                               |
| `credential_id`                                  | La credencial que autenticó (el id de la API key)                    | Separada del actor a propósito                                                                      |
| `input_digest`                                   | `sha256` del input canonicalizado y redactado                        | Compara inputs sin poder reconstruirlos                                                             |
| `result_status` / `error_code`                   | `ok` o `error`, y con qué código                                     | Las fallas también dejan fila                                                                       |
| `latency_ms`                                     | Duración de la ejecución                                             | El mismo valor que viaja en `meta.latency_ms`                                                       |
| `parent_request_id`                              | En MCP, la fila del delegado apunta a la del `execute` que lo invocó | Reconstruye la cadena meta-tool → tool del catálogo                                                 |
| `meter_category` / `billable_units` / `billable` | La clasificación de cobro                                            | La facturación (abajo)                                                                              |
| `created_at`                                     | Cuándo                                                               | Junto a un `id` monotónico para paginar por cursor                                                  |

## Atribución: humano o agente, nunca confundidos [#atribución-humano-o-agente-nunca-confundidos]

Una API key autentica como **agente**: `actor_type: "agent"`, con el id de la clave como `actor_id`. Un token OAuth o una sesión del dashboard autentican como **usuario**. En ningún caso la acción de un agente se atribuye al humano dueño de la credencial, porque la fila diría una mentira y la bitácora existe para lo contrario. Por eso `credential_id` es una columna aparte: revocar una clave no reescribe la autoría de nada.

## El input no se guarda: se guarda su huella [#el-input-no-se-guarda-se-guarda-su-huella]

`input_digest` es `sha256(canonicalize(redact(input)))`. Antes de calcular el hash, todo campo con forma de secreto (token, password, credential, api key) se reemplaza por un marcador; después, el objeto se serializa con las llaves ordenadas, para que el mismo input produzca siempre el mismo digest. El resultado permite responder «¿estas dos llamadas llevaron el mismo input?» sin poder reconstruir el input. La credencial de una conexión ni siquiera llega aquí: se resuelve server-side y jamás aparece en un resultado, en un log ni en la bitácora.

## De un `request_id` a su fila [#de-un-request_id-a-su-fila]

Toda respuesta lleva `meta.request_id`, y todo error lo repite dentro del envelope junto al `suggested_fix`. Con ese `req_…` encuentras la fila en el dashboard, y es el dato que soporte te va a pedir. El `meta.audit_status` de la respuesta cierra el círculo: `recorded` significa que la fila quedó escrita en línea; `degraded`, que el registro cayó a un respaldo durable. Una acción que ya se ejecutó con éxito nunca se convierte en un `5xx` porque falló su auditoría; ese `5xx` invitaría a reintentar una acción regulada ya hecha.

## Qué se factura [#qué-se-factura]

La bitácora es la fuente de verdad de la facturación, y la columna `billable` la calcula la base de datos, no el código de aplicación: exige `result_status = 'ok'`, una categoría facturable y `billable_units > 0`. Las consecuencias prácticas:

* **Un error nunca factura.** `billable_units` queda en 0 en toda fila fallida.
* **`meter_category` clasifica cada llamada.** `action_call`, `read_sync` y `read_query` cuentan contra el plan; `meta` (las meta-tools de MCP), `internal` (conectores abiertos como `core` e `indicadores`) y `unclassified` son overhead y no cuentan.
* **Los conectores abiertos dejan fila igual**, con `billable_units: 0`: auditar y cobrar son ejes distintos.
* **Lo que haces en tu propio dashboard no gasta tu cuota.** Una llamada con `surface: "dashboard"` o `"hosted"` se audita idéntica a cualquier otra y factura 0: guardar tu credencial o paginar tus propios datos no puede consumir la cuota de tu integración.

## Lo que no está en la bitácora [#lo-que-no-está-en-la-bitácora]

Dos registros vecinos completan el cuadro. Las **fallas de autenticación** no tienen organización que las reciba (el token no resolvió a ningún tenant), así que van a `security_events`, que guarda el prefijo de la credencial y nunca el token completo. Y los **cambios de configuración** (crear una clave, deshabilitar una conexión, rotar una credencial) van a `audit_control`, el registro del plano de control, con su `before` y su `after`.

## Dónde verla [#dónde-verla]

En [connect.emisso.ai/bitacora](https://connect.emisso.ai/bitacora): las columnas Actor, Herramienta, Scope, Plano, Resultado, Latencia, request\_id y Fecha, con filtro por API key. Cada conexión tiene además su propia pestaña de bitácora, con las ejecuciones y los cambios de configuración que la tocaron.

## Próximos pasos [#próximos-pasos]

* [Autenticación](/docs/empezar/autenticacion): quién puede escribir estas filas y cómo.
* [Conexiones](/docs/conceptos/conexiones): la unidad a la que se atribuye cada llamada.
* [Errores](/docs/operar/errores): el catálogo completo, con `suggested_fix` por código.
