/v1/account/webhook-endpoints

account/webhook-endpoints

Webhooks: adónde envía Monitoring las nuevas Signals y cómo las verificas.

Costes y límites

Costes y límites
Qué Créditos
Gestionar endpoints de webhook y entregas gratis

Límite: 30 solicitudes por minuto. Crear, modificar y eliminar: 10 por minuto. Comprobar, probar y nuevo secreto: 6 por minuto.

Admin token o clave API

Endpoints

Método
Método Ruta Para qué Acceso
GET /v1/account/webhook-endpoints Todos los endpoints de webhook, sin los archivados. Clave API o admin token
POST /v1/account/webhook-endpoints Crear un endpoint de webhook. Admin token
POST /v1/account/webhook-endpoints/{id}/verify Verificar. Clave API o admin token
POST /v1/account/webhook-endpoints/{id}/test Enviar un mensaje de prueba. Clave API o admin token
POST /v1/account/webhook-endpoints/{id}/rotate-secret Nuevo secreto de firma. Admin token
POST /v1/account/webhook-endpoints/{id}/enable Activar. Admin token
POST /v1/account/webhook-endpoints/{id}/disable Desactivar. Admin token
DELETE /v1/account/webhook-endpoints/{id} Archivar. Admin token
GET /v1/account/webhook-deliveries Las últimas 50 entregas. Clave API o admin token
POST /v1/account/webhook-deliveries/{id}/retry Volver a enviar una entrega fallida. Clave API o admin token
GET /v1/account/webhook-events Los últimos 50 eventos. Clave API o admin token

Crear un endpoint de webhook

curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints" \
  -H "Authorization: Bearer TU_ADMIN_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM-Sync",
    "url": "https://hooks.example.com/handelsregister",
    "headers": {
        "X-Receiver-Token": "TU_RECEIVER_TOKEN"
    }
}'
Parámetros
Nombre Tipo Descripción Ejemplo
Idempotency-KeyHeader stringobligatorio Un valor único por operación, idealmente un UUID. Consulta Idempotency-Key.Límites: 1 a 128 caracteres req-20260802-01
nameBody stringobligatorio Un nombre para tu propia referencia.Límites: 1 a 120 caracteres CRM-Sync
urlBody string (uri)obligatorio La dirección de tu servidor: accesible públicamente, https en el puerto 443, con un nombre de host en lugar de una dirección IP.Límites: 1 a 2.048 caracteres https://hooks.example.com/handelsregister
headersBody objetoopcional Headers propios que se envían con cada mensaje, como un token para tu servidor.Límites: como máximo 10 entradas {"X-Receiver-Token":"TU_RECEIVER_TOKEN"}
1{2  "endpoint": {3    "id": "wep_01m6b3j1g0cdz98hw8zdefbf52",4    "name": "CRM-Sync",5    "url": "hooks.example.com/…",6    "status": "pending_verification",7    "verified_at": null,8    "header_names": [9      "X-Receiver-Token"10    ],11    "consecutive_failures": 0,12    "created_at": "2026-11-01T08:28:01+00:00"13  },14  "signing_secret": "whsec_…",15  "meta": {16    "message": "Endpoint created. Store the signing secret now — it is shown only once.",17    "request_credit_cost": 0,18    "credits_remaining": 14519  }20}
201 · Respuesta de ejemplo: un endpoint de webhook nuevo, con el secreto abreviado aquí

Verificar

1{2  "id": "msg_01m6b3jsqq7ntwzkdj099ayraz",3  "type": "endpoint.verification",4  "timestamp": "2026-11-01T08:28:26.743Z",5  "schema_version": 1,6  "data": {7    "challenge": "189fb34b2af819bb13cb47d9806dbf30"8  }9}
El mensaje que recibe tu servidor durante la verificación
curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/verify" \
  -H "x-api-key: TU_CLAVE_API" \
  -H "Idempotency-Key: $(uuidgen)"
1{2  "verified": true,3  "endpoint": {4    "id": "wep_01m6b3j1g0cdz98hw8zdefbf52",5    "name": "CRM-Sync",6    "url": "hooks.example.com/…",7    "status": "active",8    "verified_at": "2026-11-01T08:28:26+00:00",9    "header_names": [10      "X-Receiver-Token"11    ],12    "consecutive_failures": 0,13    "created_at": "2026-11-01T08:28:01+00:00"14  },15  "meta": {16    "request_credit_cost": 0,17    "credits_remaining": 14518  }19}
200 · Respuesta de ejemplo a una verificación correcta
1{2  "delivery": {3    "id": "del_01m6b3jsyrnc3sz1mt31dq6107",4    "message_id": "msg_01m6b3jsynxzhvagm7n2nq7mpv",5    "event_id": "evt_01m6b3jsynxzhvagm7n2nq7mpt",6    "event_type": "endpoint.test",7    "endpoint_id": "wep_01m6b3j1g0cdz98hw8zdefbf52",8    "status": "succeeded",9    "attempts": 1,10    "last_status_code": 200,11    "last_error_category": null,12    "next_attempt_at": "2026-11-01T08:28:26+00:00",13    "succeeded_at": "2026-11-01T08:28:26+00:00",14    "created_at": "2026-11-01T08:28:26+00:00"15  },16  "meta": {17    "request_credit_cost": 0,18    "credits_remaining": 14519  }20}
200 · Respuesta de ejemplo a un mensaje de prueba

Verificar la firma

Header
Header Significado
webhook-id ID del mensaje (msg_…). Se mantiene igual en los reintentos. Úsalo para detectar duplicados.
webhook-timestamp Momento de este intento de entrega en segundos Unix.
webhook-signature Una o varias firmas de la forma v1,<Base64>, separadas por espacios.
import base64, hashlib, hmac, time

def verify_webhook(secret: str, headers: dict, body: bytes) -> bool:
    msg_id = headers["webhook-id"]
    timestamp = headers["webhook-timestamp"]
    # Rechaza mensajes de más de cinco minutos
    if abs(time.time() - int(timestamp)) > 300:
        return False
    key = base64.b64decode(secret.removeprefix("whsec_"))
    signed = f"{msg_id}.{timestamp}.".encode() + body
    expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
    # Durante una rotación del secreto, el header lleva dos firmas
    for part in headers["webhook-signature"].split(" "):
        version, _, signature = part.partition(",")
        if version == "v1" and hmac.compare_digest(signature, expected):
            return True
    return False

Payload

Campo
Campo Significado
id ID del mensaje, igual al header webhook-id.
event_id ID del evento (evt_…).
type Tipo del evento, consulta abajo.
timestamp Cuándo detectamos el evento, en UTC.
schema_version Versión de la estructura, actualmente 1.
data Los datos del evento.
Tipos de evento
Tipo de evento Significado
organization.signal.detected Un monitor ha encontrado una señal nueva. data.signal tiene la misma estructura que una señal en signals, más data.monitor y, en data.links.signal, el enlace a la señal en la API.
organization.signal.sample Un ejemplo del historial real de la empresa, enviado desde Cuenta › Monitoring. Misma estructura que arriba, más data.sample: true.
endpoint.test Mensaje de prueba mediante /test.
endpoint.verification Challenge durante la verificación.
1{2  "id": "msg_01m65pqg9x0176cteqvtxw68sa",3  "event_id": "evt_01m65pqg9sx7mwmw0edc1mfbbe",4  "type": "organization.signal.sample",5  "timestamp": "2026-10-30T06:07:37.000Z",6  "schema_version": 1,7  "data": {8    "monitor": {9      "id": "mon_01m65pn3vm11sjjp1j076cbvec",10      "type": "organization.signals"11    },12    "sample": true,13    "signal": {14      "event": {15        "id": "36c7c8fddf99bb0299d6815bcaa01013",16        "topic": "TRANSFORMATIONS",17        "topic_name": {18          "de": "Umwandlungen & M&A",19          "en": "Transformations & M&A"20        },21        "occurred_on": "2022-12-22",22        "announced_on": "2023-08-10",23        "date_basis": "SOURCE_EVENT"24      },25      "organization": {26        "type": "ORGANIZATION",27        "entity_id": "94eb910fef13f94585c48c263fa446d4",28        "current_profile": {29          "name": "Hetzner Online GmbH",30          "registration": {31            "register_type": "HRB",32            "register_number": "6089",33            "court": "Ansbach",34            "register_locality": "Ansbach",35            "registration_authority_id": "D3201",36            "register_entity_name": "Ansbach HRB 6089"37          },38          "legal_form": {39            "name": {40              "de": "Gesellschaft mit beschränkter Haftung"41            },42            "short": {43              "de": "GmbH"44            }45          },46          "seat": {47            "city": "Gunzenhausen"48          },49          "status": {50            "code": "ACTIVE"51          }52        }53      },54      "parties": {55        "counterparty": {56          "entity": {57            "type": "ORGANIZATION",58            "entity_id": "dd4d2d6854b2dbd1ffce74e669f3852e",59            "current_profile": {60              "name": "Headtrip GmbH",61              "registration": {62                "register_type": "HRB",63                "register_number": "33432",64                "court": "Nürnberg",65                "register_locality": "Nuremberg",66                "registration_authority_id": "D3310",67                "register_entity_name": "Nürnberg HRB 33432"68              },69              "legal_form": {70                "name": {71                  "de": "Gesellschaft mit beschränkter Haftung"72                },73                "short": {74                  "de": "GmbH"75                }76              },77              "seat": {78                "city": "Nürnberg"79              },80              "status": {81                "code": "INACTIVE"82              }83            }84          },85          "relationship": "TRANSFERRING_COMPANY"86        }87      },88      "register_entry": {89        "entry_number": "3",90        "entry_date": "2023-08-10",91        "description_text": "Verschmelzung zur Aufnahme der Headtrip GmbH",92        "context": "PRIMARY",93        "flags": []94      },95      "source": {96        "kind": "COMMERCIAL_REGISTER"97      },98      "details": {99        "schema": "TRANSFORMATION",100        "kind": "MERGER",101        "specifics": {102          "merger_target": "ABSORPTION"103        },104        "direction": "INCOMING",105        "is_cross_border": false,106        "resolution_date": "2022-12-22",107        "agreement_date": "2022-12-22"108      }109    },110    "links": {111      "signal": "https://handelsregister.ai/api/v1/signals/36c7c8fddf99bb0299d6815bcaa01013"112    }113  }114}
Mensaje de ejemplo para Hetzner Online GmbH, Ansbach HRB 6089: la fusión de Headtrip GmbH, Nürnberg HRB 33432, enviada como ejemplo del historial

Entregas y reintentos

Entregamos cada mensaje al menos una vez, pero no en un orden fijo. Detecta duplicados por webhook-id. Responde rápido y procesa el mensaje solo después.
Estado de una entrega
Estado Significado
withheld Espera hasta que se haya cobrado la comprobación.
pending Espera a ser enviada.
in_flight Enviándose ahora mismo.
retry_wait Espera el siguiente intento.
succeeded Entregada.
exhausted Todos los intentos fallaron.
blocked_endpoint El endpoint de webhook no puede recibir nada ahora mismo.
cancelled Cancelada, por ejemplo porque el mensaje caducó.

Activar, desactivar, archivar

curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/disable" \
  -H "Authorization: Bearer TU_ADMIN_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"

Errores

Errores
Estado Causa Qué puedes hacer
400 api_key está en la URL. Envía la clave en el header x-api-key.
400 La URL no está permitida como destino, un header no está permitido, se ha alcanzado el número máximo o el endpoint de webhook no está activo. meta.message indica el motivo. Corrige el valor o verifica primero.
400 El Idempotency-Key tiene un formato no válido. Corrige el formato, por ejemplo usa un UUID.
401 Ninguna clave API ni token, o uno no válido. Envía la clave en el header x-api-key.
403 Al bearer token le falta el permiso account:read. Usa la clave API o un token con account:read.
403 La dirección de email de tu cuenta aún no está confirmada, o la cuenta está bloqueada. Abre el enlace del email de confirmación. Si la cuenta está bloqueada, el soporte puede ayudarte.
403 Esta llamada necesita el admin token. No basta una clave API, ni tampoco otro token. Envíalo con Authorization: Bearer y el admin token.
403 Al bearer token le falta el permiso monitoring:manage. Usa la clave API o el admin token.
404 Ningún endpoint de webhook o entrega con este ID. Revisa el ID.
409 El Idempotency-Key ya se usó con datos distintos, tiene más de 24 horas o aún se está procesando. Usa una clave nueva o espera un momento.
422 Falta un valor o no es válido, como name o url. errors nombra el campo. Corrige el valor y reintenta con una clave nueva.
422 La verificación falló. Revisa el header del challenge y el estado 2xx, y reintenta con una clave nueva.
428 Falta el header Idempotency-Key. Envía el header.
429 Demasiadas solicitudes. Reintenta tras Retry-After.
503 Verificar, probar y reenviar no están disponibles temporalmente. Reintenta un poco más tarde.
1{2  "error": "invalid_request",3  "meta": {4    "message": "The endpoint is not active.",5    "request_credit_cost": 0,6    "credits_remaining": 1457  }8}
400 · Un endpoint de webhook aún no verificado no puede recibir un mensaje de prueba