# Webhooks

Webhook configuration for receiving real-time event notifications from FluxiQ PIX.

## Prerequisites

- HTTPS endpoint accessible from FluxiQ PIX backend
- Ability to verify HMAC-SHA256 signatures
- Understanding of idempotent event processing

## Webhook Architecture

```mermaid
graph LR
    PIX[FluxiQ PIX] -->|HTTPS POST| WH[Your Webhook Endpoint]
    WH -->|200 OK| PIX
    PIX -->|Retry on failure| WH
```

## Security

### HMAC-SHA256 Signature

Every webhook request includes three security headers:

| Header | Description |
|--------|-------------|
| `X-FluxiQ-Signature` | HMAC-SHA256 of the request body using the shared secret |
| `X-FluxiQ-Timestamp` | Unix timestamp of when the event was generated |
| `X-FluxiQ-Event-Id` | Unique event identifier (UUID) for idempotency |

### Signature Verification

```python
import hmac
import hashlib

def verify_signature(payload, signature, secret, timestamp):
    # Verify timestamp is within 5 minutes
    if abs(time.time() - int(timestamp)) > 300:
        return False

    # Compute HMAC
    message = f"{timestamp}.{payload}".encode()
    expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()

    return hmac.compare_digest(signature, expected)
```

```elixir
# Elixir verification
def verify_webhook(payload, signature, secret, timestamp) do
  message = "#{timestamp}.#{payload}"
  expected = :crypto.mac(:hmac, :sha256, secret, message) |> Base.encode16(case: :lower)
  Plug.Crypto.secure_compare(signature, expected)
end
```

## Event Types

### Transaction Events

| Event | Trigger |
|-------|---------|
| `transaction.created` | New PIX transaction created |
| `transaction.processing` | Transaction accepted for processing (ACSP) |
| `transaction.settled` | Transaction settled (STLD) |
| `transaction.rejected` | Transaction rejected (RJCT) |
| `transaction.returned` | Transaction returned (RTRN) |
| `transaction.cancelled` | Transaction cancelled (CANC) |

### DICT Events

| Event | Trigger |
|-------|---------|
| `key.created` | PIX key registered |
| `key.deleted` | PIX key deleted |
| `claim.created` | Ownership claim initiated |
| `claim.resolved` | Claim resolved |

### Settlement Events

| Event | Trigger |
|-------|---------|
| `settlement.cycle_completed` | Settlement cycle finished |
| `settlement.reconciliation_discrepancy` | Discrepancy found |

## Payload Format

```json
{
  "event": "transaction.settled",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-02-09T14:30:00Z",
  "data": {
    "transaction_id": "uuid",
    "end_to_end_id": "E12345678202602091430abcdef123456",
    "amount": 10000,
    "status": "STLD",
    "debtor": {
      "name": "John Doe",
      "cpf_cnpj": "12345678901",
      "ispb": "12345678"
    },
    "creditor": {
      "name": "Jane Doe",
      "cpf_cnpj": "98765432100",
      "ispb": "87654321"
    },
    "settled_at": "2026-02-09T14:30:01.200Z"
  }
}
```

## Retry Policy

| Attempt | Delay | Total Wait |
|---------|-------|------------|
| 1 | Immediate | 0s |
| 2 | 10 seconds | 10s |
| 3 | 30 seconds | 40s |
| 4 | 2 minutes | 2m 40s |
| 5 | 10 minutes | 12m 40s |
| 6 (final) | 30 minutes | 42m 40s |

After all retries are exhausted, the event is sent to the dead letter queue.

## Idempotency

Use `X-FluxiQ-Event-Id` to deduplicate events on your end. The same event may be delivered multiple times due to retries.

```python
def handle_webhook(event):
    event_id = request.headers["X-FluxiQ-Event-Id"]

    # Check if already processed
    if already_processed(event_id):
        return Response(status=200)

    # Process event
    process(event)
    mark_processed(event_id)
    return Response(status=200)
```

## Expected Outcome

After configuring webhooks:

- Real-time event notifications delivered to your endpoint
- HMAC-SHA256 signatures verified for security
- Idempotent processing using event IDs
- Automatic retries on delivery failures
