Endpoints para administrar tus destinos de webhook y auditar las entregas. El formato del evento y cómo validarlo están en Eventos y payload y Verificar la firma.
Los mismos endpoints existen en el dashboard con autenticación de usuario: es la misma lógica y el mismo shape de respuesta, así que lo que hagas por API se ve en el panel y viceversa.
El objeto endpoint
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
id | string | siempre | whep_… |
object | string | siempre | Constante "webhook_endpoint". |
url | string | siempre | Destino https al que se hace el POST. |
events | string[] | siempre | Tipos de evento suscriptos. |
status | enum | siempre | "active" o "disabled". |
description | string | si se envió | Nota tuya para identificarlo. 1 a 200 caracteres. |
secret | string | siempre | El secreto de firma. Completo sólo al crear y al rotar; en cualquier otra respuesta viene enmascarado como whsec_...abcd. |
previous_secret_expires_at | string | sólo al rotar | Hasta cuándo se firma también con el secreto anterior. |
created_at | string | siempre | ISO 8601 UTC. |
updated_at | string | siempre | ISO 8601 UTC. |
Registrar un endpoint
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
url | string | sí | URL https pública. Se rechazan: http, credenciales embebidas (https://user:pass@…), hosts internos (localhost, *.internal) e IPs privadas, de loopback o link-local. |
events | string[] | sí | De 1 a 20 tipos de evento. Ver la lista en Eventos y payload. |
description | string | no | 1 a 200 caracteres. |
curl -sS -X POST "$KYCAR_API/v1/webhook-endpoints" \
-H "Authorization: Bearer $KYCAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.tu-empresa.com/hooks/kycar",
"events": [
"verification_session.approved",
"verification_session.rejected",
"verification_session.review_required"
],
"description": "Backend de onboarding"
}'
201 Created:
{
"id": "whep_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"object": "webhook_endpoint",
"url": "https://api.tu-empresa.com/hooks/kycar",
"events": [
"verification_session.approved",
"verification_session.rejected",
"verification_session.review_required"
],
"status": "active",
"description": "Backend de onboarding",
"secret": "whsec_kR7mQ2vLxK9sYdN1bTfR4mHc8jWgE3aZ",
"created_at": "2026-08-19T14:10:02.117Z",
"updated_at": "2026-08-19T14:10:02.117Z"
}
Listar endpoints
Acepta limit y cursor. El secret viene enmascarado.
{
"data": [
{
"id": "whep_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"object": "webhook_endpoint",
"url": "https://api.tu-empresa.com/hooks/kycar",
"events": ["verification_session.approved"],
"status": "active",
"secret": "whsec_...E3aZ",
"created_at": "2026-08-19T14:10:02.117Z",
"updated_at": "2026-08-19T14:10:02.117Z"
}
],
"has_more": false
}
Actualizar un endpoint
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
url | string | no | Mismas reglas que al crear. Cambiar la URL resetea el circuit breaker del endpoint. |
events | string[] | no | Reemplaza la lista completa. De 1 a 20. |
status | enum | no | "active" o "disabled". |
description | string | no | 1 a 200 caracteres. |
Tenés que mandar al menos un campo; un body vacío devuelve 422. Es un patch
parcial pese al verbo: los campos que no mandás quedan como están.
curl -sS -X PUT "$KYCAR_API/v1/webhook-endpoints/whep_01K2M4P6R8T0V2X4Z6B8D0F2H4" \
-H "Authorization: Bearer $KYCAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "disabled"}'
Eliminar un endpoint
Devuelve 204 sin cuerpo. Si sólo querés cortar entregas de forma temporal,
preferí status: "disabled".
Probar un endpoint
Encola una entrega de prueba real: sale por el mismo camino y con la misma firma que un evento de producción, así podés validar tu verificación de firma antes de tener una sesión.
curl -sS -X POST "$KYCAR_API/v1/webhook-endpoints/whep_01K2M4P6R8T0V2X4Z6B8D0F2H4/test" \
-H "Authorization: Bearer $KYCAR_API_KEY"
202 Accepted:
{
"object": "webhook_test",
"endpoint_id": "whep_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"event_id": "evt_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"queued": true
}
El cuerpo que recibe tu endpoint:
{
"id": "evt_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"object": "event",
"api_version": "v1",
"type": "test",
"created_at": "2026-08-19T14:31:08.442Z",
"data": {
"object": {
"message": "Webhook de prueba de kyc.ar: si leés esto, la firma y la entrega funcionan.",
"webhook_id": "whep_01K2M4P6R8T0V2X4Z6B8D0F2H4"
}
}
}
| Campo | Tipo | Status | Descripción |
|---|---|---|---|
202 | Encolada | 202 | La entrega es asíncrona: el resultado queda en el log de entregas. |
conflict | 409 | 409 | El endpoint está disabled. Activalo antes de probarlo. |
not_found | 404 | 404 | El endpoint no existe. |
rate_limited | 429 | 429 | Más de 10 pruebas o re-entregas por minuto en la cuenta. |
Las pruebas ignoran el circuit breaker a propósito —son una herramienta de diagnóstico— y tampoco lo alimentan.
Rotar el secreto de firma
Genera un secreto nuevo y devuelve el valor completo una sola vez.
{
"id": "whep_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"object": "webhook_endpoint",
"url": "https://api.tu-empresa.com/hooks/kycar",
"events": ["verification_session.approved"],
"status": "active",
"secret": "whsec_2vLxK9sYdN1bTfR4mHc8jWgE3aZkR7mQ",
"previous_secret_expires_at": "2026-08-20T14:33:51.000Z",
"created_at": "2026-08-19T14:10:02.117Z",
"updated_at": "2026-08-19T14:33:51.204Z"
}
Durante 24 horas cada entrega se firma con los dos secretos: v1 con el
nuevo y v2 con el anterior. Eso te deja rotar sin ventana de caída:
- Rotás y guardás el secreto nuevo junto al viejo.
- Tu verificador acepta el evento si cualquiera de las firmas del header valida contra alguno de tus dos secretos.
- Antes de
previous_secret_expires_at, borrás el viejo.
El detalle del header con doble firma está en Verificar la firma.
Listar entregas
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
endpoint | string | sí | Id del endpoint, whep_…. Sin este parámetro la request devuelve 422. |
limit | integer | no | 1 a 100. Por defecto 25. |
cursor | string | no | Cursor opaco. |
curl -sS "$KYCAR_API/v1/webhook-deliveries?endpoint=whep_01K2M4P6R8T0V2X4Z6B8D0F2H4" \
-H "Authorization: Bearer $KYCAR_API_KEY"
{
"data": [
{
"id": "d2hlcF8wMUsyTTRQNlI4VDBWMlg0WjZCOEQwRjJINCNBIzE",
"object": "webhook_delivery",
"endpoint_id": "whep_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"event_id": "evt_7QK3M9ZP2X8V4T0R6B1D5F3H7J",
"event_type": "verification_session.approved",
"session_id": "vs_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"status": "failed",
"attempt": 3,
"http_status": 502,
"duration_ms": 218,
"error": "http_502",
"created_at": "2026-08-19T14:22:48.011Z",
"resolved_at": "2026-08-19T14:22:48.229Z"
}
],
"has_more": false
}
| Campo | Tipo | Significado | Descripción |
|---|---|---|---|
pending | status | En vuelo | El intento arrancó y todavía no se resolvió. |
delivered | status | OK | Tu endpoint respondió 2xx. |
failed | status | Falló, se reintenta | Error de red, timeout, o respuesta fuera del rango 2xx. Queda otro intento por delante. |
dead | status | Falló, sin reintentos | Era el último intento: el evento se abandona. |
El campo error es un texto corto y estable para casos frecuentes:
http_502 (respuesta no-2xx), circuit_breaker_open (el endpoint estaba
cortado), secret_decrypt_failed (problema de plataforma, no de tu endpoint).
El id de una entrega es un identificador opaco (base64url): pasalo tal cual
al endpoint de re-entrega, no lo interpretes ni lo construyas.
Re-entregar un evento
Vuelve a enviar el evento original, con el mismo event_id. Tu receptor lo
deduplica por ese id, así que re-entregar dos veces no duplica nada de tu lado.
curl -sS -X POST "$KYCAR_API/v1/webhook-deliveries/d2hlcF8wMUsy.../redeliver" \
-H "Authorization: Bearer $KYCAR_API_KEY"
202 Accepted:
{
"object": "webhook_redelivery",
"delivery_id": "d2hlcF8wMUsyTTRQNlI4VDBWMlg0WjZCOEQwRjJINCNBIzE",
"endpoint_id": "whep_01K2M4P6R8T0V2X4Z6B8D0F2H4",
"event_id": "evt_7QK3M9ZP2X8V4T0R6B1D5F3H7J",
"redelivery_id": "01K2M4P6R8T0V2X4Z6B8D0F2H4",
"queued": true
}
redelivery_id identifica este reintento (el event_id es el del evento
original y se repite). Sirve para correlacionar con el log si tenés que pedir
soporte.
| Campo | Tipo | Status | Descripción |
|---|---|---|---|
validation_failed | 422 | 422 | La entrega es de tipo test: no se re-entrega. Disparás una prueba nueva. |
not_found | 404 | 404 | La entrega o el endpoint no existen. |
conflict | 409 | 409 | El endpoint está disabled. |
rate_limited | 429 | 429 | Más de 10 pruebas o re-entregas por minuto en la cuenta. |
El payload se reconstruye en el momento a partir de la sesión: si el estado cambió desde la entrega original, la re-entrega refleja el estado actual.