An endpoint is a URL you own that receives events. You register it with your API key, choose which events it subscribes to, and Compense returns, only once, the secret it will use to sign every delivery. Everything you do here is tied to the organization that owns the key: an id from another organization responds 404.

Scopes #

ScopeAllows
webhooks:readBrowse the catalog, list and view endpoints, list and view deliveries.
webhooks:manageCreate, edit and delete endpoints, rotate the secret, send tests and retry deliveries.

A key without the scope gets 403. Request the smallest scope you need: a system that only receives events doesn’t need any; one that manages endpoints needs webhooks:manage.

Request signing

webhooks:manage is on the list of high-value operations. If your key has request signing enabled (X-Compense-Signature), these writes must be signed as well. See Authentication.

1. Browse the event catalog #

GET/v1/gateway/webhooks/event-catalog/ returns every subscribable event with its category, description, version and a payload_example, plus the legacy aliases and the signature contract. It’s the source of truth: a new event shows up there first. Filter with 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"}

Response (abridged):

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 …>", "…": "…" } }
}

The full table, by category, is in Event catalog.

2. Register the endpoint #

POST/v1/gateway/webhooks/endpoints/ registers a URL and the list of events you want to receive.

URL rules

  • It must use HTTPS and point to a public host.
  • localhost, *.local, *.internal, private IPs, loopback and link-local (169.254.*) are rejected, as are URLs with embedded credentials (https://user:password@…).

events rules

  • A non-empty list of catalog keys.
  • Legacy aliases are normalized to their canonical key (subsidy.application.received is stored as subsidies.application.received).

Limit: 20 endpoints per organization (422 WEBHOOK_ENDPOINT_LIMIT when you exceed it).

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="Accounting reconciliation",
    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"]   # ONLY returned here: store it now in your secrets manager

The response is the write envelope; the endpoint goes in 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_..."
  }
}

The secret is shown only once

data.secret only comes in the create and rotate responses. The gateway doesn’t store secrets: no read returns it, and if you repeat the creation with the same Idempotency-Key you get the original response with "idempotent_replay": true but without secret. Store it in your secrets manager (HashiCorp Vault, Azure Key Vault, Google Secret Manager…) before doing anything else. If you lost it, rotate it.

3. List and view your endpoints #

GET/v1/gateway/webhooks/endpoints/ and GET/v1/gateway/webhooks/endpoints/{endpoint_id}/ return your organization’s endpoints without the secret, with their state and statistics for the last 24 hours.

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. Edit or pause an endpoint #

PATCH/v1/gateway/webhooks/endpoints/{endpoint_id}/ is a partial update: it only accepts url, events, label, description and active. Any other field, including secret, responds 422 WEBHOOK_INVALID_FIELD (you change the secret by rotating it).

To stop receiving events without losing the history, pause with active: false; to resume, active: true.

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

A disabled endpoint (disabled by Compense) can’t be reactivated with active: true: it responds 409 WEBHOOK_ENDPOINT_DISABLED. Create another endpoint or contact support.

5. Delete an endpoint #

DELETE/v1/gateway/webhooks/endpoints/{endpoint_id}/ deletes the endpoint and its entire delivery history. It can’t be undone.

Deleting erases the history

If you only want to stop receiving events (for example, during a maintenance window), use active: false. That way you keep the deliveries for auditing and retrying.

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

The SDKs generate an Idempotency-Key automatically on every write if you don’t provide one; with cURL, send it yourself (see Idempotency).

Endpoint states #

StateWho sets itReceives eventsWhat to do
activeYou (on creation or with active: true)YesNothing.
pausedYou (active: false)NoReactivate it with active: true whenever you want.
failingCompense: with 10 or more deliveries in 24 h, less than 50% were successfulYesCheck your receiver and the delivery history.
disabledCompense (e.g. after 3 410 Gone responses)NoIt can’t be reactivated through the API: create another endpoint or contact support.

Errors #

All errors use application/problem+json with trace_id (see Errors). The engine’s code comes in detail.

HTTPCode (in detail)When
401—X-API-Key is missing.
403—The key doesn’t have the scope, is expired or revoked, or the IP isn’t allowed.
404WEBHOOK_ENDPOINT_NOT_FOUNDThe id doesn’t exist or belongs to another organization.
409WEBHOOK_ENDPOINT_DISABLEDYou tried to reactivate an endpoint disabled by Compense.
409—Same Idempotency-Key while the original request is still in progress.
422WEBHOOK_INVALID_URLURL that isn’t HTTPS, a private or internal host, or with credentials.
422WEBHOOK_INVALID_EVENTSevents empty or with keys outside the catalog.
422WEBHOOK_INVALID_FIELDNon-modifiable field (e.g. secret) or a non-boolean active.
422WEBHOOK_ENDPOINT_LIMITYour organization already has 20 endpoints.
429—Per-minute limit or monthly key quota.

From the terminal: the compense CLI #

The Python and JavaScript SDKs ship the compense CLI (in Python it’s installed with the package; in Node, npx compense). It reads the key from $COMPENSE_API_KEY and points to the sandbox by default. For this flow it has three commands:

Shell
export COMPENSE_API_KEY="cmp_..."

compense events --category wallets            # catalog (filtered)
compense endpoint-create \
  --url https://erp.nova.example/compense/webhooks \
  --event credits.decision \
  --event wallets.topup.completed \
  --label "ERP Nova"                          # prints the endpoint with its secret, ONCE
compense endpoints                            # your endpoints, without the secret

The CLI has no commands for editing, pausing, deleting or rotating: do it with the SDK or with cURL. All commands in The compense CLI.