Skip to content
Translated page. The English version is the source of truth.

POST /apiv2/reports/webhooks

Registre una URL y NETTS la llamará cuando un informe esté listo, en lugar de que usted tenga que sondear el estado.

Estos endpoints son independientes de los webhooks de órdenes. Registrarse allí no le suscribe a las notificaciones de informes, y viceversa. El formato de transmisión —firma, encabezados, comportamiento de reintentos— es idéntico, por lo que un controlador escrito para uno funciona para el otro.

URL base del endpoint

https://netts.io/apiv2/reports/webhooks

Encabezados de solicitud

EncabezadoRequeridoDescripción
X-API-KEYClave API del panel de control
X-Real-IPUna dirección de la lista blanca de la clave

Principal y de respaldo

Hasta dos endpoints por cuenta. primary recibe todo. backup se utiliza únicamente después de que la entrega al principal haya agotado los intentos — y está firmado con su propio secreto, no con el del principal.

Registrar

bash
curl -s -X POST 'https://netts.io/apiv2/reports/webhooks' \
  -H 'X-API-KEY: your-api-key' \
  -H 'X-Real-IP: 203.0.113.10' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://example.com/netts/reports", "role": "primary"}'
json
{
  "status": "success",
  "code": 10000,
  "data": {
    "id": 1,
    "url": "https://example.com/netts/reports",
    "role": "primary",
    "is_active": true,
    "created_at": "2026-09-06 17:05:12+00:00",
    "updated_at": "2026-09-06 17:05:12+00:00",
    "secret": "whsec_<64 hex characters>"
  }
}

El secreto se muestra una sola vez, aquí. Nunca se vuelve a devolver — ni mediante la lista, ni mediante el endpoint de lectura. Guárdelo cuando lo reciba. Si se pierde, emita uno nuevo:

bash
curl -s -X POST 'https://netts.io/apiv2/reports/webhooks/1/rotate-secret' \
  -H 'X-API-KEY: your-api-key' -H 'X-Real-IP: 203.0.113.10'

La rotación surte efecto inmediatamente y el secreto antiguo deja de verificar, por lo que debe implementar el nuevo valor primero si no puede tolerar una interrupción.

Administrar

MétodoRutaAcción
GET/apiv2/reports/webhookslistar los suyos, sin secretos
GET/apiv2/reports/webhooks/{id}leer uno
PATCH/apiv2/reports/webhooks/{id}cambiar url, o pausar con is_active: false
DELETE/apiv2/reports/webhooks/{id}eliminarlo

La URL debe ser HTTPS pública. Se rechazan las direcciones de bucle invertido (loopback), privadas y de enlace local, al igual que las credenciales dentro de la URL. Todo lo que sea rechazado se devuelve como 422 con el motivo. La comprobación se ejecuta nuevamente justo antes de cada entrega, por lo que un endpoint que posteriormente resuelva a una dirección privada dejará de recibir datos.

Qué enviamos

json
{
  "event": "report.ready",
  "delivery_id": 4,
  "order_id": "REPxxxxxxxxxxxx",
  "order_type": "statement",
  "client_request_id": "stmt-2026-09-usdt",
  "status": "done",
  "format": "csv",
  "download_url": "/apiv2/reports/REPxxxxxxxxxxxx/download",
  "expires_at": "2026-10-06 15:48:04+00:00",
  "artifact": { "sha256": "…", "size_bytes": 696 },
  "confirmed_at": "2026-09-06T15:48:04Z"
}
CampoDescripción
eventreport.ready — clave de enrutamiento para su controlador
delivery_idClave de deduplicación. También se envía como el encabezado X-Netts-Delivery
order_idEl número de orden que se le proporcionó cuando puso el informe en cola
order_typestatement o balance_at_date
download_urlRuta para obtener el archivo, relativa a https://netts.io
artifact.sha256Suma de comprobación, para que pueda verificar lo que descargó
confirmed_atUTC

Todas las marcas de tiempo están en UTC.

Verificación de la firma

X-Netts-Signature: sha256=<hex>
X-Netts-Timestamp: <unix seconds>
X-Netts-Delivery:  <delivery_id>

La firma es un HMAC-SHA256 sobre "<timestamp>." + raw body, calculado con el secreto del endpoint que recibió la solicitud. Compare en tiempo constante y rechace cualquier solicitud cuya marca de tiempo quede fuera de una ventana de ±5 minutos.

python
import hmac, hashlib, time

def verify(raw_body: bytes, sig_header: str, ts_header: str, secret: str) -> bool:
    if abs(time.time() - int(ts_header)) > 300:      # anti-replay
        return False
    signed = f"{ts_header}.".encode() + raw_body
    expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig_header)

Firme con el secreto de la URL en la que se recibió la solicitud: el principal y el de respaldo tienen secretos diferentes.

La entrega es al menos una vez

Una respuesta perdida provoca un reintento, por lo que el mismo evento puede llegar dos veces.

  1. Deduplique mediante delivery_id. Una repetición debe ser una operación nula (no-op) de su lado.
  2. Verifique la firma antes de procesar, no después.
  3. Responda 2xx solo después de haber almacenado el evento. Cualquier otra cosa, o un tiempo de espera agotado, se trata como un fallo y se reintenta.

Los reintentos a un endpoint se realizan a 1 minuto, 5 minutos, 15 minutos, 1 hora, 6 horas y 24 horas — seis intentos en total, abarcando un poco más de 31 horas. Cuando se agotan y ha registrado un backup, la entrega se traslada allí y el cronograma comienza de nuevo con el secreto propio del respaldo. El delivery_id se mantiene igual en todo momento, por lo que un evento que falló en el principal y tuvo éxito en el de respaldo sigue siendo un único evento.

No se siguen las redirecciones.

Límites de tasa

10 solicitudes por segundo por endpoint, compartidas entre todos los clientes.

Errores

El registro responde 201, la eliminación responde 204 sin cuerpo, todo lo demás 200.

HTTPSignificado
401clave faltante o no válida, o la IP de origen no está en la lista blanca
404no existe dicho endpoint en su cuenta
409el rol solicitado ya está ocupado — role primary is already taken
422la URL fue rechazada, o un cuerpo de PATCH no contenía nada que cambiar
429límite de tasa excedido

Una URL rechazada se devuelve como 422 con el motivo detallado, para que pueda mostrárselo a quien lo haya escrito:

json
{"status": "error", "code": -4, "msg": "url: Value error, invalid webhook_url: resolved address 127.0.0.1 is not public"}

Las formulaciones son only https:// URLs are allowed, credentials in URL are not allowed y resolved address <ip> is not public. La última se resuelve en el momento del registro y nuevamente justo antes de cada entrega, por lo que un nombre de host que posteriormente apunte a una dirección privada dejará de recibir datos.

Relacionado