Documentation
SDKs and CLI
Official libraries and the command line.
There is no official SDK package yet
FlowFinds does not publish a client library. There is no npm package, no PyPI distribution and no go get path, and this page will not name one that does not exist. If you find a package on a registry claiming to be an official FlowFinds client, it is not ours.
The HTTP interface is the supported surface. That is a smaller inconvenience than it sounds, because the interface has one authentication scheme, one content type and one error convention, and every route is documented in the API reference.
What a client actually needs
Three things, and no more than three.
- A cookie jar. The engine identifies a caller by
ff_session. It mints one on the first response that lacks it and returns it inSet-Cookie. Keep it and send it back; without it, every call is a new visitor with no hold and no claim. - JSON in, JSON out. Request bodies are a single JSON object with
Content-Type: application/json. Every response is JSON, including failures — the shared guard converts an uncaught exception anywhere in the engine into a structured error rather than an HTML page. - A status check in the body. A 200 is not a success. Read
status, and where it isrefusedorabstained, readreasonandexplanation. The full set of reason codes is served byGET /api/reasons.
Python, standard library only
The engine itself is written against the Python standard library, and a client can be too. This is a complete client.
import http.cookiejar, json, urllib.error, urllib.request
class FlowFinds:
"""Everything a FlowFinds client needs: an origin and a cookie jar."""
def __init__(self, origin):
self.origin = origin.rstrip("/")
self._jar = http.cookiejar.CookieJar()
self._opener = urllib.request.build_opener(
urllib.request.HTTPCookieProcessor(self._jar))
def _call(self, method, path, payload=None):
data = json.dumps(payload).encode() if payload is not None else None
req = urllib.request.Request(self.origin + path, data=data, method=method)
if data is not None:
req.add_header("Content-Type", "application/json")
try:
with self._opener.open(req) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
# Every route fails as structured JSON; read the body, not the code alone.
return e.code, json.load(e)
def get(self, path):
return self._call("GET", path)
def post(self, path, payload=None):
return self._call("POST", path, payload if payload is not None else {})
ff = FlowFinds(ORIGIN)
status, product = ff.get("/api/find")
status, offer = ff.get("/api/offer")
# A 200 is not a success. Check the named status in the body.
if offer.get("status") == "refused":
print(offer["failed_leg"], offer["reason"], offer["explanation"])TypeScript in a browser
In a browser the cookie is handled for you; the only thing that matters is sending it. credentials: "include" is the whole integration.
// The browser already carries the cookie. Send credentials and it works.
const ORIGIN = process.env.FLOWFINDS_ORIGIN!;
async function call<T>(method: "GET" | "POST", path: string, body?: unknown): Promise<T> {
const res = await fetch(`${ORIGIN}${path}`, {
method,
credentials: "include",
headers: body === undefined ? undefined : { "Content-Type": "application/json" },
body: body === undefined ? undefined : JSON.stringify(body),
cache: "no-store",
});
// Non-2xx bodies are JSON too. Parse before you decide what happened.
return (await res.json()) as T;
}
export const ff = {
get: <T>(path: string) => call<T>("GET", path),
post: <T>(path: string, body: unknown = {}) => call<T>("POST", path, body),
};TypeScript on a server
Outside a browser there is no cookie store, so keep the session yourself. Read it from the first response and send it on every call after that.
// Outside a browser there is no cookie store, so keep one yourself.
let session: string | undefined;
async function call(method: "GET" | "POST", path: string, body?: unknown) {
const res = await fetch(`${ORIGIN}${path}`, {
method,
headers: {
...(session ? { cookie: `ff_session=${session}` } : {}),
...(body === undefined ? {} : { "Content-Type": "application/json" }),
},
body: body === undefined ? undefined : JSON.stringify(body),
});
// The engine mints a session on the first response that lacks one.
const set = res.headers.get("set-cookie");
const minted = set?.match(/ff_session=([^;]+)/)?.[1];
if (minted) session = minted;
return { status: res.status, body: await res.json() };
}This is the pattern the product surface itself uses when it reads the engine on a founder’s behalf: the commerce tools send cookie: ff_session=… explicitly, with redirect: "error" and a response size cap, because a server-side reader should refuse a redirect rather than follow one.
Command line
There is no published FlowFinds CLI. The repositories contain operational scripts — spawning a development server under a unique name, verifying store quality, running the benchmark harness, proving the funnel ladder — but those are internal tooling for the people who build FlowFinds, not a distributed command-line client, and documenting them as one would be a fiction.
For interactive work, curl with a cookie jar is the command line. The cookbook opens with a twelve-line shell wrapper that carries the session, and every endpoint page in the reference shows its own curl invocation.
Reproducing the benchmark
The one thing you may genuinely want to run rather than call is commerce-v1, the benchmark the commerce agent is measured against. Its definition, its scenarios and its recorded runs are published, and the procedure for running it yourself is written up separately: reproduce our results.
If an SDK ships
It will be announced in the changelog and documented here, with the registry, the package name and the version. Until that entry exists, the HTTP interface is the whole of the supported surface.
Start with the quickstart, or go straight to the API reference.
Next: Quickstart · API reference · Cookbook