SDK

Node.js SDK

The official racehooks npm package is a TypeScript-first SDK for the RaceHooks API. It handles OAuth token management, request retries, and HMAC signature verification — so you can focus on building. The SDK works across all sports supported by the platform; no changes are needed when IndyCar or NASCAR feeds go live.

Node.js ≥ 18TypeScript 5.xESM + CJSZero runtime deps

Installation

bash
npm install racehooks
# or
yarn add racehooks
# or
pnpm add racehooks

Quick start

index.ts
import { RaceHooks, verifySignature } from "racehooks";

const rh = new RaceHooks({
  clientId:     process.env.RACEHOOKS_CLIENT_ID!,
  clientSecret: process.env.RACEHOOKS_CLIENT_SECRET!,
});

// Subscribe to all race events
const { webhook, webhookSecret } = await rh.webhooks.create({
  feedId:     "raceevent",
  webhookUrl: "https://yourserver.com/f1-hook",
});

// SAVE webhookSecret immediately — only shown once
console.log("Signing secret:", webhookSecret);

Verify incoming deliveries

server.ts
import { verifySignature } from "racehooks";
import express from "express";

const app = express();
app.use("/f1-hook", express.raw({ type: "application/json" }));

app.post("/f1-hook", (req, res) => {
  const sig = req.headers["x-racehooks-signature"] as string;

  if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET!)) {
    return res.status(401).send("Bad signature");
  }

  const payload = JSON.parse(req.body.toString());
  console.log("Event:", payload.event, payload.data);
  res.sendStatus(200);
});

RaceHooks constructor

FieldTypeDescription
clientIdstringYour RaceHooks OAuth client ID.
clientSecretstringYour RaceHooks OAuth client secret.
baseUrlstring?Override the API base URL. Defaults to https://api.racehooks.io.
timeoutnumber?Request timeout in milliseconds. Defaults to 10,000.
The SDK auto-refreshes your OAuth Bearer token 60 seconds before expiry and deduplicates concurrent token requests — no caching or token management needed on your side.

rh.webhooks

typescript
// List all webhooks
const { webhooks } = await rh.webhooks.list();

// Get a specific webhook
const { webhook } = await rh.webhooks.get("wh_7a3b9c2d");

// Create with filters
const { webhook: wh, webhookSecret } = await rh.webhooks.create({
  feedId:     "timingdata",
  webhookUrl: "https://your-app.com/hook",
  filters: {
    constructors: ["ferrari"],
    positions:    { min: 1, max: 5 },
  },
});

// Pause
await rh.webhooks.update("wh_7a3b9c2d", { active: false });

// Delete
await rh.webhooks.delete("wh_7a3b9c2d");

// Send a test delivery
const result = await rh.webhooks.test("wh_7a3b9c2d");

// Rotate signing secret
const { webhookSecret: newSecret } = await rh.webhooks.rotateSecret("wh_7a3b9c2d");

// Delivery logs
const { logs } = await rh.webhooks.logs("wh_7a3b9c2d", { limit: 50 });

rh.usage

typescript
// Current subscription tier and limits
const sub = await rh.usage.subscription();
console.log(sub.tier, sub.limits.maxWebhooks);

// Today's delivery count
const today = await rh.usage.current();
console.log(today.deliveryCount, today.dailyRemaining);

// Breakdown by feed
const byFeed = await rh.usage.byFeed();
byFeed.forEach(f => console.log(f.feedId, f.deliveryCount));

// Last 90 days history
const history = await rh.usage.history();

rh.simulate

typescript
// Start a simulation
const sim = await rh.simulate.start({
  sessionId: "2025-british-r1",
  speed:     10,  // 10× real-time
  feeds:     ["raceevent", "timingdata"], // omit to replay all subscribed feeds
});

console.log("Simulation started:", sim.simulationId);

// Poll for completion
let s = await rh.simulate.get(sim.simulationId);
while (s.status === "running") {
  await new Promise(r => setTimeout(r, 2000));
  s = await rh.simulate.get(sim.simulationId);
}

console.log(`Done: ${s.eventsDispatched} events, ${s.deliveryCount} deliveries`);

// List and cancel
const all = await rh.simulate.list();
await rh.simulate.cancel(sim.simulationId);

Analytics tier payloads

On the Analytics tier, timingdata, cardata, and tyrestintseries payloads are enriched with ML-computed fields. The SDK exports a WebhookPayload type that includes the analytics key.

typescript
import type { WebhookPayload } from "racehooks";

// On the Analytics tier, timingdata / cardata / tyrestintseries payloads
// include analytics.* fields per driver
app.post("/hook", (req, res) => {
  const payload: WebhookPayload = req.body;

  // analytics keyed by driver number
  const driver1 = payload.analytics?.["1"];
  if (driver1) {
    console.log({
      tireHealth:          driver1.tireHealth,          // 0–1 remaining tread
      cliffLapPredicted:   driver1.cliffLapPredicted,   // lap when cliff expected
      cliffRisk:           driver1.cliffRisk,           // "low" | "medium" | "high"
      pitStopProbability:  driver1.pitStopProbability,  // 0–1 probability next lap
      safetyCarProbability: driver1.safetyCarProbability,
    });
  }
  res.sendStatus(200);
});

CommonJS

The package ships with both ESM (dist/index.js) and CommonJS (dist/index.cjs) builds. Node.js resolves the correct format automatically based on your project's package.json type field.

javascript
// CommonJS
const { RaceHooks, verifySignature } = require("racehooks");

const rh = new RaceHooks({
  clientId:     process.env.RACEHOOKS_CLIENT_ID,
  clientSecret: process.env.RACEHOOKS_CLIENT_SECRET,
});

Error handling

The SDK throws typed errors you can catch and handle:

typescript
import { RaceHooks, RaceHooksAuthError, RaceHooksAPIError } from "racehooks";

try {
  await rh.webhooks.create({ feedId: "timingdata", webhookUrl: "..." });
} catch (err) {
  if (err instanceof RaceHooksAuthError) {
    // OAuth token request failed — check credentials
    console.error("Auth failed:", err.message);
  } else if (err instanceof RaceHooksAPIError) {
    // API returned an error response
    console.error(`API error ${err.statusCode}:`, err.message);
  }
}
← Race EventsMCP Server →