Un endpoint es una URL tuya que recibe eventos. Lo registras con tu llave de API, eliges a qué eventos se suscribe y Compense te devuelve una sola vez el secreto con el que firmará cada entrega. Todo lo que haces aquí queda ligado a la organización dueña de la llave: un id de otra organización responde 404.

Scopes #

ScopePermite
webhooks:readConsultar el catálogo, listar y ver endpoints, listar y ver entregas.
webhooks:manageCrear, editar y eliminar endpoints, rotar el secreto, enviar pruebas y reintentar entregas.

Una llave sin el scope recibe 403. Pide el menor scope que necesites: un sistema que solo recibe eventos no necesita ninguno; uno que administra endpoints necesita webhooks:manage.

Firma de peticiones

webhooks:manage está en la lista de operaciones de alto valor. Si tu llave tiene activada la firma de peticiones (X-Compense-Signature), estas escrituras también deben ir firmadas. Ver Autenticación.

1. Consulta el catálogo de eventos #

GET/v1/gateway/webhooks/event-catalog/ devuelve todos los eventos suscribibles con su categoría, descripción, version y un payload_example, más los alias legados y el contrato de firma. Es la fuente de verdad: un evento nuevo aparece primero ahí. Filtra con category (credits, subsidies, monetary_allowance, affiliates, wallets, loans, health, admin, pqrs, general).

import os
from compense_gateway import CompenseClient

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

catalogo = client.webhooks.event_catalog(category="wallets")
for ev in catalogo["events"]:
    print(ev["event"], ev["category"], ev["version"])
print(catalogo["aliases"])     # {"subsidy.application.received": "subsidies.application.received"}

Respuesta (resumida):

JSON
{
  "count": 14,
  "categories": { "wallets": ["wallets.cashin.initiated", "wallets.cashin.confirmed", "…"] },
  "events": [
    {
      "event": "wallets.topup.completed",
      "label": "…",
      "description": "…",
      "category": "wallets",
      "since": "2026-10-27",
      "version": "1",
      "payload_example": { "event": "wallets.topup.completed", "version": "1", "tenant_id": "…", "occurred_at": "…", "resource": { "…": "…" } }
    }
  ],
  "aliases": { "subsidy.application.received": "subsidies.application.received" },
  "signature": { "algorithm": "HMAC-SHA256", "headers": { "X-Webhook-Signature": "sha256=<hex …>", "…": "…" } }
}

La tabla completa, por categoría, está en Catálogo de eventos.

2. Registra el endpoint #

POST/v1/gateway/webhooks/endpoints/ registra una URL y la lista de eventos que quieres recibir.

Reglas de la URL

  • Debe usar HTTPS y apuntar a un host público.
  • Se rechazan localhost, *.local, *.internal, las IP privadas, loopback y link-local (169.254.*), y las URL con credenciales embebidas (https://usuario:clave@…).

Reglas de events

  • Lista no vacía de claves del catálogo.
  • Los alias legados se normalizan a su clave canónica (subsidy.application.received se guarda como subsidies.application.received).

Límite: 20 endpoints por organización (422 WEBHOOK_ENDPOINT_LIMIT al pasarlo).

from compense_gateway import WebhookEndpoint

tx = client.webhooks.endpoints.create(
    url="https://erp.nova.example/compense/webhooks",
    events=["credits.decision", "wallets.topup.completed"],
    label="ERP Nova",
    description="Conciliación contable",
    idempotency_key="nova-wh-erp-001",
)
endpoint = WebhookEndpoint.from_dict(tx.data)
print(endpoint.id, endpoint.status, endpoint.timeout_seconds, endpoint.max_retries)

secreto = tx.data["secret"]   # SOLO viene aquí: guárdalo ya en tu gestor de secretos

La respuesta es el sobre de escritura; el endpoint va en data:

JSON
{
  "status": "success",
  "transaction_id": "a1b2c3d4-0000-4a1b-9c2d-000000000010",
  "module": "webhooks",
  "data": {
    "id": "3f1a2b3c-0000-4a1b-9c2d-000000000001",
    "url": "https://erp.nova.example/compense/webhooks",
    "label": "ERP Nova",
    "events": ["credits.decision", "wallets.topup.completed"],
    "active": true,
    "status": "active",
    "timeout_seconds": 10,
    "max_retries": 5,
    "stats": { "deliveries_count_24h": 0, "success_rate_24h": 100.0, "last_delivery_at": null },
    "secret": "whsec_..."
  }
}

El secreto se muestra una sola vez

data.secret solo viene en la respuesta de crear y de rotar. El gateway no guarda secretos: ninguna lectura lo devuelve, y si repites la creación con la misma Idempotency-Key recibes la respuesta original con "idempotent_replay": true pero sin secret. Guárdalo en tu gestor de secretos (HashiCorp Vault, Azure Key Vault, Google Secret Manager…) antes de hacer cualquier otra cosa. Si lo perdiste, rótalo.

3. Lista y consulta tus endpoints #

GET/v1/gateway/webhooks/endpoints/ y GET/v1/gateway/webhooks/endpoints/{endpoint_id}/ devuelven los endpoints de tu organización sin secreto, con su estado y estadísticas de las últimas 24 horas.

for ep in client.webhooks.endpoints.list():
    print(ep.id, ep.status, ep.url, ep.events)

ep = client.webhooks.endpoints.get("3f1a2b3c-0000-4a1b-9c2d-000000000001")
print(ep.raw["stats"])          # deliveries_count_24h, success_rate_24h, last_delivery_at…

4. Edita o pausa un endpoint #

PATCH/v1/gateway/webhooks/endpoints/{endpoint_id}/ es una actualización parcial: solo acepta url, events, label, description y active. Cualquier otro campo, incluido secret, responde 422 WEBHOOK_INVALID_FIELD (el secreto se cambia rotándolo).

Para dejar de recibir eventos sin perder el historial, pausa con active: false; para volver, active: true.

tx = client.webhooks.endpoints.update(
    "3f1a2b3c-0000-4a1b-9c2d-000000000001",
    events=["credits.decision", "credits.disbursed"],
    active=False,                               # pausar
    idempotency_key="nova-wh-erp-001-pausa",
)
print(tx.data["status"])                        # paused

Un endpoint disabled (deshabilitado por Compense) no se reactiva con active: true: responde 409 WEBHOOK_ENDPOINT_DISABLED. Crea otro endpoint o escribe a soporte.

5. Elimina un endpoint #

DELETE/v1/gateway/webhooks/endpoints/{endpoint_id}/ elimina el endpoint y todo su historial de entregas. No se puede deshacer.

Eliminar borra el historial

Si solo quieres dejar de recibir eventos (por ejemplo, durante una ventana de mantenimiento), usa active: false. Así conservas las entregas para auditar y reintentar.

tx = client.webhooks.endpoints.delete("3f1a2b3c-0000-4a1b-9c2d-000000000001")
print(tx.status)

Los SDK generan una Idempotency-Key automáticamente en cada escritura si no le das una; en cURL envíala tú (ver Idempotencia).

Estados de un endpoint #

EstadoQuién lo poneRecibe eventosQué hacer
activeTú (al crear o con active: true)SíNada.
pausedTú (active: false)NoReactívalo con active: true cuando quieras.
failingCompense: con 10 o más entregas en 24 h, menos del 50 % fue exitosaSíRevisa tu receptor y el historial de entregas.
disabledCompense (p. ej. tras 3 respuestas 410 Gone)NoNo se reactiva por API: crea otro endpoint o escribe a soporte.

Errores #

Todos los errores usan application/problem+json con trace_id (ver Errores). El código del motor viene en detail.

HTTPCódigo (en detail)Cuándo
401—Falta X-API-Key.
403—La llave no tiene el scope, está vencida o revocada, o la IP no está permitida.
404WEBHOOK_ENDPOINT_NOT_FOUNDEl id no existe o es de otra organización.
409WEBHOOK_ENDPOINT_DISABLEDIntentaste reactivar un endpoint deshabilitado por Compense.
409—Misma Idempotency-Key con la petición original aún en curso.
422WEBHOOK_INVALID_URLURL que no es HTTPS, host privado o interno, o con credenciales.
422WEBHOOK_INVALID_EVENTSevents vacío o con claves fuera del catálogo.
422WEBHOOK_INVALID_FIELDCampo no modificable (p. ej. secret) o active no booleano.
422WEBHOOK_ENDPOINT_LIMITTu organización ya tiene 20 endpoints.
429—Límite por minuto o cuota mensual de la llave.

Desde la consola: la CLI compense #

Los SDK de Python y JavaScript traen la CLI compense (en Python se instala con el paquete; en Node, npx compense). Lee la llave de $COMPENSE_API_KEY y apunta al sandbox por defecto. Para este flujo tiene tres órdenes:

Shell
export COMPENSE_API_KEY="cmp_..."

compense events --category wallets            # catálogo (filtrado)
compense endpoint-create \
  --url https://erp.nova.example/compense/webhooks \
  --event credits.decision \
  --event wallets.topup.completed \
  --label "ERP Nova"                          # imprime el endpoint con su secreto, UNA vez
compense endpoints                            # tus endpoints, sin secreto

La CLI no tiene órdenes para editar, pausar, eliminar ni rotar: hazlo con el SDK o con cURL. Todas las órdenes en La CLI compense.