Hay comportamientos que solo aparecen con el paso del tiempo: una cuota que pasa a mora, una obligación que vence, un subsidio que exige permanencia. Probarlos esperando días reales no es opción. Un reloj de prueba (test clock) es una fuente de tiempo virtual: lo fijas en una fecha (frozen_time) y lo adelantas las veces que quieras; su current_time es el “ahora” de tu escenario.

Solo con llaves de sandbox

Las operaciones de relojes ( POST/v1/gateway/sandbox/test-clocks/ , GET/v1/gateway/sandbox/test-clocks/{clock_id}/ y

POST/v1/gateway/sandbox/test-clocks/{clock_id}/advance/ ) responden 403 a una llave de producción, sin importar la URL.

Cómo funcionan #

Los relojes no guardan estado en el servidor. Todo el estado (la fecha fija y los segundos avanzados) viaja dentro del id, con la forma tcl-<datos>-<firma>:

  • Cada advance devuelve un reloj con un id NUEVO. Guarda siempre el id que te devuelve la última llamada y continúa desde él. El id viejo sigue siendo válido, pero representa el reloj antes del avance.
  • El id lleva una firma de integridad: si lo editas a mano (por ejemplo, para “retroceder” el tiempo) el gateway lo rechaza con 422.
  • Como el resultado depende solo del id y del avance, el mismo advance sobre el mismo id da siempre el mismo reloj. Por eso estas rutas no usan Idempotency-Key.
  • Solo avanzan: delta_seconds debe ser >= 0 y como máximo 63 días (5.443.200 segundos) por llamada. Para saltos mayores, avanza varias veces.
  • frozen_time acepta ISO-8601 (2026-10-01T00:00:00Z) o segundos Unix. Una fecha sin zona horaria se interpreta en UTC.
sequenceDiagram
    participant T as Tu prueba
    participant G as Gateway (sandbox)
    T->>G: create(frozen_time = 2026-10-01T00:00:00Z)
    G-->>T: tcl-A (current_time 2026-10-01, advanced 0)
    T->>G: advance(tcl-A, 30 días)
    G-->>T: tcl-B (current_time 2026-10-31, advanced 2.592.000)
    T->>G: advance(tcl-B, 15 días)
    G-->>T: tcl-C (current_time 2026-11-15, advanced 3.888.000)
    T->>G: get(tcl-B)
    G-->>T: tcl-B sigue en 2026-10-31

Crear, adelantar y consultar #

import os
from compense_gateway import CompenseClient

client = CompenseClient(api_key=os.environ["COMPENSE_API_KEY"], environment="sandbox")

reloj = client.sandbox.test_clocks.create(frozen_time="2026-10-01T00:00:00Z")
print(reloj.id, reloj.current_time)                       # tcl-… 2026-10-01T00:00:00+00:00

reloj = client.sandbox.test_clocks.advance(reloj.id, delta_seconds=30 * 86400)
print(reloj.id, reloj.current_time, reloj.advanced_seconds)   # id NUEVO, +30 días

mismo = client.sandbox.test_clocks.get(reloj.id)
print(mismo.frozen_time, mismo.current_time, mismo.status)    # ready

El reloj tiene esta forma (en cURL, create y advance lo devuelven dentro de data del sobre de escritura y get lo devuelve directo; los SDK desenvuelven el sobre por ti):

JSON
{
  "id": "tcl-MTc5MDgxMjgwMC4yNTkyMDAw-9fd1bf0d88220638",
  "object": "test_clock",
  "status": "ready",
  "frozen_time": "2026-10-01T00:00:00+00:00",
  "current_time": "2026-10-31T00:00:00+00:00",
  "advanced_seconds": 2592000,
  "drives": ["loan_aging_dpd", "provisioning_rollforward", "subsidy_permanence"],
  "environment": "sandbox"
}
CampoQué es
idEl reloj. Cambia en cada avance.
frozen_timeLa fecha en que lo fijaste al crearlo. No cambia.
advanced_secondsTotal de segundos avanzados desde frozen_time.
current_timefrozen_time + advanced_seconds: el “ahora” de tu escenario.
drivesPara qué está pensado: envejecimiento de mora (loan_aging_dpd), provisiones (provisioning_rollforward) y permanencia de subsidios (subsidy_permanence).

El reloj es tu fuente de 'ahora', no el del gateway

En esta versión ninguna otra operación del sandbox recibe el id de un reloj: las fechas de cartera, salud o PQRS del sandbox son fijas (por ejemplo, la primera cuota de los préstamos de prueba vence el 15 de julio de 2026). Usa current_time como el “ahora” de tu sistema durante la prueba (inyéctalo en tu servicio de fechas) y compáralo con las fechas que devuelve el sandbox.

Caso de uso: mora de cartera #

Quieres probar que tu sistema clasifica bien la mora de una cuota según los días vencidos (0, 30, 60, 90 días o más). La primera cuota de los préstamos de prueba vence el 15 de julio de 2026. Fija un reloj ese día y adelántalo por tramos; como cada llamada admite como máximo 63 días, el ejemplo avanza en saltos y evalúa tu función de clasificación en cada current_time.

Avanzar 95 días en saltos de máximo 63
from datetime import datetime

MAX = 63 * 86400
reloj = client.sandbox.test_clocks.create(frozen_time="2026-07-15T00:00:00Z")
vence = datetime.fromisoformat(reloj.frozen_time)

pendiente = 95 * 86400
while pendiente > 0:
    paso = min(pendiente, MAX)
    reloj = client.sandbox.test_clocks.advance(reloj.id, delta_seconds=paso)
    pendiente -= paso
    dias = (datetime.fromisoformat(reloj.current_time) - vence).days
    print(reloj.current_time, dias, clasificar_mora(dias))     # tu función

Combínalo con el préstamo de prueba en mora de 95 días (CC 1000000004, estado DEFAULTED) para comparar tu cálculo con lo que devuelve la cartera del sandbox.

Otros escenarios típicos:

  • Vencimientos: fija el reloj el día antes del vencimiento de una obligación, avanza 1 día y verifica que tu sistema genera el aviso o el recargo.
  • Recordatorios: avanza en saltos de 24 horas y comprueba que tu tarea programada envía cada recordatorio una sola vez.
  • Permanencia: avanza meses (en saltos de hasta 63 días) para probar reglas que exigen un tiempo mínimo de afiliación.

Errores #

HTTPCuándo
403La llave es de producción.
422frozen_time vacío o no es ISO-8601 ni segundos Unix; el id no es un reloj (tcl-…) o fue alterado; delta_seconds negativo, no entero o mayor que 63 días.

Notas por SDK #

  • Python: advance(..., by_seconds=...) es el nombre deprecado (1.0) de delta_seconds; advance(..., to=...) no existe en el gateway y lanza CompenseConfigError; create(name=...) se ignora con un aviso (los relojes no tienen nombre). El modelo es TestClock (id, frozen_time, current_time, advanced_seconds, status, raw).
  • JavaScript / TS: create({ frozenTime }) acepta texto ISO-8601 o segundos Unix.
  • PHP: create() acepta texto, segundos Unix (int) o \DateTimeInterface.
  • .NET: CreateAsync tiene sobrecargas para string, long (segundos Unix) y DateTimeOffset; AdvanceAsync(id, deltaSeconds) recibe long.
  • Go: CreateTestClockParams.FrozenTime es un time.Time (se envía como RFC 3339 en UTC); AdvanceTestClockParams.DeltaSeconds es int64.
  • CLI: compense clock create|get|advance (ver La CLI compense).