# HFO Pulse — VIP API Documentation

**Real-time cascade detection for BTC/USDT.**  
VIP API access provides two delivery channels: **WebSocket** (real-time push) and **Webhook** (server-to-server POST).

---

## Authentication

All VIP API requests require your API key in the `X-API-Key` header:

```
X-API-Key: vip_your_api_key_here
```

Your VIP API key is provided when you subscribe to the VIP plan.

---

## Channel 1: WebSocket (Real-Time)

Connect to receive cascade events the instant they are detected. Sub-50ms delivery.

### Connection

```
wss://hfopulse.com/ws
```

> **Note:** the endpoint is the `/ws` path on port 443, not port 3001. Port 3001
> is the internal plaintext listener and is not reachable from outside the host.

### Authentication Flow

```json
// 1. Connect to the WebSocket URL
// 2. Send authentication message:
{"type": "auth", "apiKey": "vip_your_api_key_here"}

// 3. Receive confirmation:
{"type": "connected", "data": {"role": "vip", "message": "Authenticated successfully"}}
```

If you do not authenticate within 5 seconds, the connection is downgraded to the
free tier (direction-only payloads). You can send `auth` at any time afterwards to
upgrade — the connection is not dropped.

### Events

| Event | Description | When |
|---|---|---|
| `cascade_detected` | New cascade detected | ~13/day |
| `cascade_outcome` | Signal graded with results | After each signal completes |
| `exit_warning` | Cascade momentum fading | During active signals |
| `regime_change` | Market regime shifted | On regime transitions |
| `heartbeat` | Connection alive check | Every 30 seconds |

### Event Payloads

#### `cascade_detected`
```json
{
  "event": "cascade_detected",
  "signalId": "sig_1718600000000_a3f2e1",
  "timestamp": 1718600000000,
  "data": {
    "symbol": "BTCUSDT",
    "direction": "SHORT",
    "confidence": 93,
    "grade": "EXTREME",
    "price": 65769.8,
    "timing": "PRE_CASCADE",
    "estimatedMagnitude": 0.45,
    "market": {
      "regime": "NORMAL",
      "session": "US",
      "headFakeEstimate": 0.163,
      "expectedCascade": 0.173,
      "regimeDetectionRate": 66,
      "cascadeWindow": "5-15 min",
      "tension": "ELEVATED",
      "manipulation": "HIGH"
    },
    "action": {
      "bias": "SHORT",
      "protect": "Close/protect LONG positions",
      "entry": "Enter SHORT on confirmation after head-fake"
    }
  }
}
```

**Field Reference:**

| Field | Type | Description |
|---|---|---|
| `direction` | `LONG` \| `SHORT` | Detected cascade direction |
| `confidence` | `0-100` | Detection confidence score |
| `grade` | `LOW` \| `MEDIUM` \| `HIGH` \| `EXTREME` | Confidence tier |
| `price` | `number` | BTC price at detection |
| `timing` | `PRE_CASCADE` \| `MID_CASCADE` \| `LATE_CASCADE` | Where in the cascade lifecycle |
| `estimatedMagnitude` | `number \| null` | Estimated % price move |
| `market.regime` | `LOW_VOL` \| `NORMAL` \| `HIGH_VOL` \| `PANIC` | Current market volatility regime |
| `market.session` | `US` \| `EUROPE` \| `ASIA` | Active trading session |
| `market.headFakeEstimate` | `number` | Mean adverse excursion before the cascade, measured for this regime (0.163 = 0.163%) |
| `market.expectedCascade` | `number` | Mean favourable excursion measured for this regime |
| `market.regimeDetectionRate` | `number` | % of signals in this regime that produced a real event |
| `market.cascadeWindow` | `string` | Observed time-to-peak range for this regime |
| `market.tension` | `LOW` \| `MODERATE` \| `ELEVATED` \| `CRITICAL` | Market stress level |
| `market.manipulation` | `NONE` \| `MODERATE` \| `HIGH` \| `EXTREME` | Detected order book manipulation |
| `action.bias` | `LONG` \| `SHORT` | Directional bias |
| `action.protect` | `string` | Risk management suggestion |
| `action.entry` | `string` | Entry timing suggestion |

#### `cascade_outcome`
```json
{
  "event": "cascade_outcome",
  "signalId": "sig_1718600000000_a3f2e1",
  "timestamp": 1718601200000,
  "data": {
    "direction": "SHORT",
    "grade": "STRONG",
    "mfePercent": 0.38,
    "maePercent": 0.07,
    "cascadeMagnitude": 0.38,
    "durationSeconds": 1200
  }
}
```

| Field | Description |
|---|---|
| `grade` | `EXCELLENT` \| `STRONG` \| `CORRECT` \| `WEAK` \| `WRONG` |
| `mfePercent` | Maximum Favorable Excursion (best price in signal direction) |
| `maePercent` | Maximum Adverse Excursion (worst price against signal) |
| `durationSeconds` | Signal observation window |

#### `exit_warning`
```json
{
  "event": "exit_warning",
  "signalId": "sig_1718600000000_a3f2e1",
  "timestamp": 1718600600000,
  "data": {
    "type": "PROFIT_FADE",
    "direction": "SHORT",
    "currentMFE": 0.35,
    "fadePercent": 40,
    "recommendation": "Cascade momentum fading — consider taking profits or tightening stops"
  }
}
```

#### `regime_change`
```json
{
  "event": "regime_change",
  "timestamp": 1718600000000,
  "data": {
    "newRegime": "HIGH_VOL",
    "previousRegime": "NORMAL"
  }
}
```

### Reconnection

If your WebSocket disconnects, reconnect and call the REST endpoint to check what you missed:

```
GET /api/vip/signals/latest?count=5
Header: X-API-Key: vip_your_key
```

---

## Channel 2: Webhook (Server-to-Server)

HFO Pulse POSTs cascade events directly to your server. You provide a URL, we push events to it.

### Register Your Webhook

```bash
curl -X POST https://hfopulse.com/api/vip/webhook \
  -H "X-API-Key: vip_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook/hfo",
    "events": ["cascade_detected", "cascade_outcome", "exit_warning"]
  }'
```

**Response:**
```json
{
  "success": true,
  "webhookId": "wh_abc123",
  "hmacSecret": "your_hmac_secret_for_verification",
  "message": "Webhook registered. Cascade events will be POSTed to your URL.",
  "events": ["cascade_detected", "cascade_outcome", "exit_warning"]
}
```

### Delivery Headers

| Header | Description |
|---|---|
| `X-Signature` | `t=<unix_seconds>,v1=<hmac_sha256>` — see below |
| `X-Hfo-Timestamp` | Unix seconds when the payload was signed |
| `X-Hfo-Event` | Event type (`cascade_detected`, …) |
| `X-Hfo-Signal-Id` | Correlation id — matches `signalId` in the body |
| `X-Hfo-Delivery` | Unique per delivery attempt; use it to dedupe retries |

### Verifying Payloads

`X-Signature` is `t=<unix_seconds>,v1=<hex_hmac>`. The HMAC-SHA256 is computed
over the string `<timestamp>.<raw_request_body>`, keyed with your `hmacSecret`.

Because the timestamp is inside the signed string, it cannot be tampered with —
reject any payload whose timestamp is outside a tolerance window (we recommend
5 minutes) to prevent replay of a captured request.

**Verify against the raw body bytes, not a re-serialised object.**

**Python verification:**
```python
import hmac, hashlib, time

def verify(payload_bytes, signature_header, secret, tolerance=300):
    parts = dict(p.split("=", 1) for p in signature_header.split(","))
    ts, provided = parts.get("t"), parts.get("v1")
    if not ts or not provided:
        return False
    if abs(time.time() - int(ts)) > tolerance:
        return False  # replay
    signed = ts.encode() + b"." + payload_bytes
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, provided)
```

**Node.js verification:**
```javascript
const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map(p => p.split('='))
  );
  const ts = parseInt(parts.t, 10);
  if (!ts || !parts.v1) return false;
  if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false; // replay

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex');

  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

### Filtering (recommended)

By default you receive every signal. Across 1637 live signals, restricting
delivery to volatile regimes lifts detection quality substantially:

| Filter | Detection rate | EXCELLENT+STRONG |
|---|---|---|
| No filter | 67.6% | 22% |
| `regimes: ["PANIC","HIGH_VOL"]` | **78%** | **38%** |
| `LOW_VOL` only (for reference) | 45% | 4% |

Filters are applied server-side, so filtered signals never reach your endpoint.

```bash
curl -X POST https://hfopulse.com/api/vip/webhook \
  -H "X-API-Key: vip_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook/hfo",
    "events": ["cascade_detected", "cascade_outcome", "exit_warning"],
    "filters": {
      "regimes": ["PANIC", "HIGH_VOL"],
      "minConfidence": 90,
      "minGrade": "EXTREME",
      "directions": ["LONG", "SHORT"]
    }
  }'
```

| Filter | Type | Applies to |
|---|---|---|
| `minConfidence` | `0-100` | `cascade_detected` |
| `minGrade` | `HIGH` \| `EXTREME` | `cascade_detected` |
| `regimes` | array of `LOW_VOL`/`NORMAL`/`HIGH_VOL`/`PANIC` | all except `regime_change` |
| `directions` | array of `LONG`/`SHORT` | any event with a direction |

Only the event types listed above are available. Any other value is ignored and
returned in a `rejectedEvents` field on the registration response.

### Retry Policy

| Attempt | Delay | Timeout |
|---|---|---|
| 1st | Immediate | 5s |
| 2nd | 1 second | 5s |
| 3rd | 2 seconds | 5s |

If all three attempts fail, the endpoint enters **exponential backoff** —
1 min, 2 min, 4 min … capped at 1 hour. A single successful delivery clears it.
After 10 consecutive failed deliveries the webhook is deactivated; you can
re-enable it yourself:

```bash
curl -X POST https://hfopulse.com/api/vip/webhook/reactivate \
  -H "X-API-Key: vip_your_api_key"
```

Your current health — including `backingOff`, `retryAfter`, `consecutiveFailures`
and `lastFailure` — is always visible on `GET /api/vip/status`.

### Rotating Your Secret

Re-registering a webhook keeps your existing secret. To roll it deliberately:

```bash
curl -X POST https://hfopulse.com/api/vip/webhook/rotate-secret \
  -H "X-API-Key: vip_your_api_key"
```

### Remove Webhook

```bash
curl -X DELETE https://hfopulse.com/api/vip/webhook \
  -H "X-API-Key: vip_your_api_key"
```

---

## REST Endpoints

### `GET /api/vip/signals/latest`

Returns recent signals for reconnection after WebSocket disconnect.

```bash
curl https://hfopulse.com/api/vip/signals/latest?count=5 \
  -H "X-API-Key: vip_your_api_key"
```

### `GET /api/vip/status`

Returns your API key status, webhook health, and delivery stats.

```bash
curl https://hfopulse.com/api/vip/status \
  -H "X-API-Key: vip_your_api_key"
```

**Response:**
```json
{
  "key": "vip_a1b2c3d4...",
  "active": true,
  "plan": "VIP",
  "webhook": {
    "url": "https://your-server.com/webhook/hfo",
    "events": ["cascade_detected", "cascade_outcome", "exit_warning"],
    "active": true
  },
  "deliveryCount": 47,
  "lastSeen": 1718600000000
}
```

---

## Rate Limits

| Channel | Limit |
|---|---|
| WebSocket | Unlimited receive (push-only). Max 5 concurrent connections per key. |
| Webhook | Unlimited (event-driven, ~13 signals/day). |
| REST | 300 requests/minute per key. |

---

## Example Code

Working client examples are available in the `docs/examples/` directory:

- **Python WebSocket**: `websocket_client.py` — pip install websocket-client
- **Node.js WebSocket**: `websocket_client.js` — npm install ws
- **Python Webhook Receiver**: `webhook_receiver.py` — pip install flask

---

## Support

- **Email**: support@hfopulse.com
- **Discord**: VIP channel
- **Status**: `GET /api/vip/status`
