Render a Lookout event list with suggested taste clusters in a React app — in 30 minutes.
This walkthrough goes from “credentials in hand” to “a working Lookout
view in my own React app.” It deliberately ignores most of the API
surface so you can ship something today and grow from there.
We won’t put credentials in the browser. We proxy the API through a tiny
Node server that signs in, holds the token, and injects the two required
headers. For a real partner integration this is non-negotiable.
Create server.ts:
server.ts
import express from "express";import fetch from "node-fetch";const app = express();const BASE = process.env.FD_BASE_URL!;// Sign in once, cache the session. (Production: add proactive refresh —// see /cookbook/token-refresh.)let accessToken = "";async function signIn() { const r = await fetch(`${BASE}/auth`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: process.env.FD_USERNAME, password: process.env.FD_PASSWORD, }), }); const data = await r.json(); if (!data.AccessToken) throw new Error(`Sign-in failed: ${data.Challenge ?? r.status}`); accessToken = data.AccessToken;}app.use("/api/*", async (req, res) => { if (!accessToken) await signIn(); const qs = req.url.split("?")[1]; const url = `${BASE}/${req.params[0]}${qs ? "?" + qs : ""}`; const upstream = await fetch(url, { method: req.method, headers: { Authorization: `Bearer ${accessToken}`, "X-Preferred-Partner-Id": process.env.FD_PARTNER_ID!, "Content-Type": "application/json", }, }); res.status(upstream.status) .type(upstream.headers.get("content-type") ?? "application/json") .send(await upstream.text());});app.listen(8787, () => console.log("FD proxy on :8787"));
Run it: node --loader ts-node/esm server.ts.Point Vite’s dev server at it via vite.config.ts:
Now the browser hits /api/... and the proxy injects Authorization: Bearer … + X-Preferred-Partner-Id. The same pattern works behind
Next.js, Remix, Express, or anything else.
suggested_tcs returns only cluster references. For a predicted-outcome
summary, follow up with GET /events/{eid}/campaigns_expected_value
(returns { expected_value: "<string>" }) and render that string.