# Recibir eventos con webhooks

Recibe actualizaciones firmadas de facturas y cumplimiento sin consultar repetidamente la API.

Los webhooks avisan a tu integración cuando ocurre algo importante en una cuenta Live o Test. Son especialmente útiles para los resultados asíncronos de verificación de NIF y VERI*FACTU y para los movimientos de saldo, pero también informan sobre clientes, facturas, rectificaciones y PDF.

## Crea un destino de Test

Crea el destino con una clave de API de Test para que las pruebas no se mezclen con eventos Live. Suscríbete solo a los eventos que entienda tu integración; usa `"*"` únicamente cuando quieras recibir deliberadamente todos los tipos actuales y futuros.

### cURL

```bash
curl https://api.fiscalrail.com/v1/event-destinations \
  --request POST \
  --header "Authorization: Bearer ak_test_..." \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Invoice updates",
    "url": "https://example.com/webhooks/fiscalrail",
    "enabled_events": [
      "invoice.amended",
      "invoice.verifactu_registration.accepted",
      "invoice.verifactu_registration.accepted_with_errors",
      "invoice.verifactu_registration.rejected"
    ]
  }'
```

La respuesta contiene un secreto de firma `whsec_...`. Guárdalo de forma segura. FiscalRail puede devolverlo al consultar ese destino, pero lo omite en los listados.

El endpoint debe usar HTTPS público. Durante el desarrollo local, expón el controlador mediante un túnel HTTPS de confianza y sustituye la URL del destino cuando cambie.

## Verifica antes de interpretar

FiscalRail firma el cuerpo exacto de la petición y envía el resultado en `FiscalRail-Signature`. Verifica la firma antes de interpretar el JSON o empezar el trabajo.

### Python

```python
import hashlib
import hmac
import time


def verify_fiscalrail_signature(raw_body, header, secret):
    parts = dict(item.split("=", 1) for item in header.split(","))
    timestamp = int(parts["t"])
    if abs(time.time() - timestamp) > 300:
        return False

    signed = str(timestamp).encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])
```

### Ruby

```ruby
require "openssl"

def valid_fiscalrail_signature?(raw_body, header, secret)
  parts = header.split(",").to_h { _1.split("=", 2) }
  timestamp = Integer(parts.fetch("t"))
  return false if (Time.now.to_i - timestamp).abs > 300

  expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{raw_body}")
  OpenSSL.fixed_length_secure_compare(expected, parts.fetch("v1"))
rescue KeyError, ArgumentError
  false
end
```

La firma cubre `timestamp + "." + raw_body` mediante HMAC-SHA256. Rechaza marcas de tiempo con más de cinco minutos y compara las firmas en tiempo constante. Cada reintento recibe una marca de tiempo y una firma nuevas.

## Confirma la recepción rápidamente

Devuelve cualquier respuesta `2xx` en cuanto hayas autenticado el evento y lo hayas guardado de forma duradera en una cola. Realiza el trabajo lento después de confirmarlo.

La entrega es al menos una vez y no está ordenada. Guarda el identificador del evento antes de procesarlo e ignora los que ya hayas tratado. No supongas que `invoice.issued` llegará antes que `invoice.amended` ni que un resultado de VERI*FACTU llegará inmediatamente después de la emisión.

Los payloads de webhook son deliberadamente breves. Identifican el evento y el recurso relacionado, pero omiten la instantánea inmutable `data`. Consulta el evento por su identificador cuando necesites la instantánea histórica o el recurso relacionado cuando necesites su estado actual.

Suscribe un destino Live a `billing.balance_transaction.created` para conciliar
los cargos de uso y los abonos de saldo. Consulta el evento completo para leer
la instantánea inmutable de la transacción. En un cargo de uso, `source_event`
identifica el evento de dominio que originó el cargo.

## Prueba el flujo completo

Emite o corrige una factura en la misma cuenta de Test. Tu endpoint debería recibir el evento correspondiente. Comprueba que el controlador:

1. verifica el cuerpo original y la marca de tiempo;
2. registra cada identificador de evento una sola vez;
3. devuelve `2xx` rápidamente;
4. consulta el evento o la factura relacionada de forma asíncrona;
5. ignora de forma segura los tipos de evento que no conoce.

FiscalRail intenta cada entrega fallida hasta cinco veces. Si todos los intentos siguen fallando durante 24 horas, desactiva el destino. Corrige el endpoint y vuelve a activar el destino mediante la API o el panel.

Consulta la [referencia de eventos](/en/docs/api/events) para conocer el envelope y la [referencia de webhooks](/en/docs/api/event-destinations) para ver los campos del destino y las reglas de entrega.
