RRelo Docs
API & GuíasGuías

Motor de dinero y trazabilidad

Cómo opera el motor único de comisiones y margen en Go, qué garantías de trazabilidad existen (email_log, audit_events, timeline) y cómo verificarlas desde ops.

Motor de dinero y trazabilidad

Desde julio 2026 la plataforma tiene un solo ejecutor de dinero (el motor Go del backbone) y tres capas de trazabilidad que cubren cada acción que toca dinero o configuración. Esta guía es para operación: qué hace cada pieza, dónde mirar y cómo verificar que está sana.

Vista general

Diagrama interactivo
Cargando diagrama…

Un solo motor de dinero

El motor de comisiones en Go (backbone/internal/commission/, endpoint GET /commission/calculate en ingest.relo.mx) es el único ejecutor de la cadena de tasas y caps. Todos los consumidores del worker (planner, portal de partner, generación de pagos, AI assistant) pasan por el selector de engine.

Cadena de tasas, en orden de prioridad por fila:

  1. Tasa congelada — si ya existe un partner_payments para ese periodo, gana siempre (inmutable).
  2. rate_override por tiersmatch_rules con rule_data.tiers[], retroactivo a la primera unidad del mes.
  3. rate_override plano — el específico por CID gana sobre el partner-wide; campaign-scoped gana sobre client-level.
  4. Override de segmentopartner_segment_commissions con ventanas effective_from/to.
  5. Sin tasa — la fila aporta unidades y revenue pero cero comisión (no hay default de cliente en el lado del costo).

Semántica de caps: NULL, 0 y >0 son cosas distintas

Los caps de presupuesto son asimétricos entre partner y cliente — es el footgun más caro de la plataforma:

TablaNULL0>0
partner_segment_budgets.budget_amount (costo)Ilimitado (no hay cap, auto-pause no tiene dónde disparar)No paga nada (kill switch)Cap normal: payable = min(bruto, cap)
client_relo_budgets.budget_amount (ingreso)Ingreso ilimitadoIngreso ilimitadoCap normal

Además: carve-outs por CID (budget_override con cid_token) capean independiente; la fórmula final es Σ(carve_capped) + min(restante_bruto, cap_segmento) + locked_commission. Las ventanas budget_start_from / paused_at excluyen filas completas.

Estado del soak de paridad

  • El selector COMMISSION_ENGINE (worker var) está en go desde 2026-05-03; cada call site conserva un override por llamada { engine: 'parity' } para rollback.
  • Soak hasta el 2026-08-06: el parity sweep corre cada 6h comparando Go vs SQL RPC (tolerancia $0.01). Drift → audit_events con actor = 'commission_parity' (cero hasta ahora).
  • Después del soak: se quitan los overrides, se congela el RPC como histórico read-only y se retira el sweep.

Pagos congelados son inmutables

Las tasas se congelan en partner_payments al generar el cierre y nunca se recalculan en su lugar. Al aprobar (pending → approved) se re-congela desde el engine en vivo salvo que exista adjusted_commission; si el engine falla, el pago conserva sus datos congelados. Para recalcular un cierre: borrar el pago y regenerar (scripts/regenerate-payments.js --via=go).

/margin/summary: una sola respuesta de margen

GET /margin/summary en el backbone devuelve por cliente/segmento/periodo:

  • revenue, income_gross, income_cap, income_capped
  • cost_gross, cost_capped, cost_unlimited
  • margin, margin_pct, payout_ratio (= costo capped / income cap; ~10% es sano)
  • Flag de riesgo capped_income_unlimited_cost: el cuadrante peligroso — ingreso capeado con costo ilimitado, donde cada venta extra quema margen. La protección ahí es el auto-pause + cascada (pausa a todos los partners cuando el cap del cliente se agota).

Los datos de margen van detrás de requireSensitiveAccess: los partners nunca los ven.

Trazabilidad: las tres capas

1. email_log — cada correo saliente

Todo email que manda la plataforma queda registrado con HTML completo, template, destinatarios, mailgun_id y status (sent/failed). Lo escribe sendEmailAndLog; si un flujo manda correo, hay fila.

2. Trigger de auditoría — cada mutación de dinero/config

Un trigger de base de datos escribe una fila en audit_events (actor = 'db_trigger', con before/after en JSONB) por cada INSERT/UPDATE/DELETE en las 9 tablas sensibles:

partner_segment_budgets, partner_segment_commissions, client_relo_budgets, match_rules, partner_clients, partners, campaigns, client_segments, client_segment_rules.

Cubre por construcción todo lo que toque esas tablas: handlers del worker, MCP write tools, consola SQL, onboard_client().

3. Timeline unificado

Un solo feed de auditoría por entidad que mezcla mutaciones, paridad de comisiones, pausas de presupuesto y correos. Lo renderiza TimelineFeed.tsx en el perfil de partner y de cliente. Los partners solo ven filas marcadas como partner-visible.

Guard de cobertura en generación de pagos

Antes de generar un cierre, handleGeneratePayments compara el revenue del espejo gated contra el gross del periodo. Si la cobertura es menor al 10% (firma de un ledger de la marca que no llegó), la respuesta incluye un coverage_warningavisa pero nunca bloquea (los flujos de advance son legítimos). Si ves montos de cierre sospechosamente bajos, este warning es lo primero a revisar.

Quickrefs de ops

Consultar el email log (admin)

# Lista (sin html_body), filtros opcionales partner_id / client_id / q / limit / offset
curl -H "Authorization: Bearer $TOKEN" \
  "https://relo-api.quomx.workers.dev/api/email-log?client_id=3&limit=50"

# Detalle con HTML completo
curl -H "Authorization: Bearer $TOKEN" \
  "https://relo-api.quomx.workers.dev/api/email-log/<id>"

Como partner (solo sus propios correos): GET /api/partners/:id/email-log y GET /api/partners/:id/email-log/:emailId.

Consultar el timeline

curl -H "Authorization: Bearer $TOKEN" \
  "https://relo-api.quomx.workers.dev/api/partners/<id>/timeline?limit=50&from=2026-07-01"

curl -H "Authorization: Bearer $TOKEN" \
  "https://relo-api.quomx.workers.dev/api/clients/<id>/timeline?types=mutation,email"

Parámetros: limit, offset, types, from, to.

Reconstruir agregados

Los agregados ClickHouse→Supabase son cachés reconstruibles. Si algo se ve inconsistente (drift de dashboard, hueco de backfill), se regeneran con un solo path:

curl -X POST "https://ingest.relo.mx/sync/rebuild" \
  -H "Content-Type: application/json" \
  -d '{"client_id": 3, "from": "2026-07-01", "to": "2026-07-31"}'

Dónde cae el drift

Todo drift o evento automático aterriza en audit_events. Actores a filtrar:

ActorQué reporta
commission_parityDrift Go vs SQL RPC en el parity sweep (objetivo: cero hasta 2026-08-06)
margin_summaryEventos del endpoint de margen
db_triggerMutaciones en las 9 tablas sensibles (before/after JSONB)
budget_parity_drift.*Drift del modo BUDGET_ENGINE=parity
pause_partner_segmentAuto-pause / cascada de presupuestos

Verificación rápida de salud

  1. Paridad limpia: audit_events sin filas nuevas de commission_parity.
  2. Margen coherente: /margin/summary de un mes cerrado cuadra con GlobalFinancials del dashboard.
  3. Sin cuadrante peligroso: revisar capped_income_unlimited_cost por segmento; si aparece, confirmar que el auto-pause/cascada está armado.
  4. Cierres con cobertura: ningún coverage_warning en la generación del mes.
  5. Emails trazados: un correo reciente conocido (p.ej. aprobación de pago) aparece en /api/email-log.

On this page