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.

  1. A cookie jar. The engine identifies a caller by ff_session. It mints one on the first response that lacks it and returns it in Set-Cookie. Keep it and send it back; without it, every call is a new visitor with no hold and no claim.
  2. 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.
  3. A status check in the body. A 200 is not a success. Read status, and where it is refused or abstained, read reason and explanation. The full set of reason codes is served by GET /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