Live Threat Feed

A WebSocket feed of threat intelligence as it is ingested: new entities, entities reported as malicious, and new relationships between entities.

The feed is live-only. It delivers what happens after you connect, not a replay of history — use Corpus Analytics GET /recent for the backlog.

Base URL: https://apis.threatwinds.com/api/analytics/v1

Why a ticket is required

Browsers cannot set request headers on a WebSocket handshake, so the usual Authorization header is not available. Putting a bearer token in the query string instead would leak it into access logs, proxy logs and Referer headers, and it would stay valid long after.

Instead you authenticate over ordinary HTTP to obtain a single-use ticket, then open the socket with it. The ticket expires in 30 seconds and is consumed the first time it is redeemed, so a captured ticket is near-worthless.

Step 1 — Mint a ticket

Endpoint: GET /live/ticket

Header Type Required Description
Authorization string Optional Bearer token from an active session.
api-key string Optional API key.
api-secret string Optional API secret.
curl -H "Authorization: Bearer $TOKEN" \
  "https://apis.threatwinds.com/api/analytics/v1/live/ticket"
{ "ticket": "8Jx1c...", "expires_in": 30 }

The ticket records the security groups of the caller at the moment it was minted. Those groups decide what the socket will deliver.

Step 2 — Open the socket

Endpoint: GET /live (WebSocket upgrade)

Parameter Type Required Description
ticket string Yes The ticket from /live/ticket.
const { ticket } = await fetch('/api/analytics/v1/live/ticket', {
  headers: { Authorization: `Bearer ${token}` },
}).then(r => r.json())

const ws = new WebSocket(
  `wss://apis.threatwinds.com/api/analytics/v1/live?ticket=${encodeURIComponent(ticket)}`,
)

ws.onmessage = (e) => {
  const event = JSON.parse(e.data)
  console.log(event.type, event.value, event.reputation)
}

Because a ticket is single-use, reconnecting requires minting a new one.

Event format

Each message is one JSON object.

Field Type Description
type string entity.created, entity.malicious, or entity.linked.
time string RFC 3339 timestamp of the event.
entityId string The entity’s identifier. For entity.linked, the source entity.
entityType string Entity type, e.g. ip, domain, md5.
value string The entity’s value, e.g. 203.0.113.10.
reputation int Reported reputation, -3..+3.
tags string[] Tags supplied at report time, when present.
toEntityId string entity.linked only — the target entity.
mode string entity.linked only — association or aggregation.
{
  "type": "entity.malicious",
  "time": "2026-07-27T19:24:17.588Z",
  "entityId": "ip-ad0c2ed9a0a9b2382...",
  "entityType": "ip",
  "value": "203.0.113.10",
  "reputation": -3,
  "tags": ["c2"]
}

An entity reported with a negative reputation produces two events: entity.created and entity.malicious. Subscribe to whichever suits you and ignore the other — entity.malicious alone is the signal-only view.

Visibility

You only receive events for entities whose security groups intersect the groups recorded in your ticket. Unauthenticated tickets carry the public group and therefore see only public entities.

Security group membership is not disclosed in the event payload.

Connection handling

Behaviour Detail
Keepalive The server sends a WebSocket ping every 30 seconds. Standard browser clients answer automatically.
Slow consumers A client that cannot keep up has events dropped rather than delaying other subscribers. The feed is lossy under load by design.
Capacity Each instance serves a bounded number of concurrent feeds and replies 503 when full.

Error Codes

Status Code Description Possible Cause
401 Unauthorized Missing, expired, or already-redeemed ticket
503 Service Unavailable Live feed disabled, or the instance is at capacity