FlashFender · Public docs

Inventory embed & API

Same Active lot, three ways to consume it: WordPress HTML tag, React mount of that tag, or JSON (public token or v1 API key). Lists are newest first by listed_at. One token or key is one rooftop — never another dealer’s stock.

Copy your embed token from Settings → Website inventory embed. Do not commit live tokens. Example rooftop id (Drip): 4d43b08c-3d56-4b3f-b465-c8dd5d50e62e.

WordPress (Custom HTML)

Paste into a Custom HTML block. Cards open the hosted VDP unless you set data-vdp-base. data-theme="auto" inherits the host page. New cars get a badge when listed_at is within data-new-days (default 14). Replace the token with yours from Settings.

Custom HTML block
<!-- FlashFender inventory embed -->
<div
  data-adaptus-inventory
  data-dealership="4d43b08c-3d56-4b3f-b465-c8dd5d50e62e"
  data-token="YOUR_EMBED_TOKEN"
  data-limit="24"
  data-theme="auto"
  data-new-days="14"
></div>
<script async src="https://app.flashfender.com/embed/inventory.js"></script>

Iframe alternative (when the CMS blocks third-party scripts) is on Settings → Website. Allow script or iframe from https://app.flashfender.com.

React: mount the same tag

This is a DOM widget, not a React component. Render the div, then load inventory.js once in useEffect. Theme inherit still applies. Prefer native fetch (next section) if you want your own cards and routing.

useEffect mount
import { useEffect } from "react";

export function InventoryWidget() {
  useEffect(() => {
    const src = "https://app.flashfender.com/embed/inventory.js";
    if (document.querySelector(`script[src="${src}"]`)) {
      window.AdaptusInventoryEmbed?.refresh();
      return;
    }
    const script = document.createElement("script");
    script.src = src;
    script.async = true;
    document.body.appendChild(script);
  }, []);

  return (
    <div
      data-adaptus-inventory
      data-dealership="YOUR_DEALERSHIP_ID"
      data-token="YOUR_EMBED_TOKEN"
      data-limit="24"
      data-theme="auto"
      data-new-days="14"
    />
  );
}

React: fetch JSON

Recommended for a React site. Public API is CORS-open, Active-only, VIN masked. Sort is newest-first (sort=listed_at desc). Use since for units listed after a timestamp.

Fetch with listed_at / since
const params = new URLSearchParams({
  token: "YOUR_EMBED_TOKEN",
  dealership_id: "YOUR_DEALERSHIP_ID",
  sort: "listed_at",
  limit: "50",
  offset: "0",
});
// Incremental new units (ISO 8601). Alias: listed_after
params.set("since", lastListedAtIso);

const res = await fetch(
  `https://app.flashfender.com/api/vehicles/public?${params}`,
  { credentials: "omit" }
);
const json = await res.json();
// json.data: { id, year, make, model, listed_at, updated_at, photos, … }
// json.count, json.limit, json.offset — newest listed_at first

Public API (embed token)

GET https://app.flashfender.com/api/vehicles/public

  • Auth: token= your token from Settings (query param). Scope with dealership_id or slug.
  • CORS: Access-Control-Allow-Origin: *, GET/OPTIONS only.
  • Payload: Active vehicles only. listed_at (flash / lot date, ISO 8601), updated_at, masked VIN. Default order: newest listed_at first.
  • Dates: since / listed_after → listed_at > since. updated_since → price/photo edits.
  • Filters: make, model, year, q, min_price, max_price, min_km, max_km, body_style, transmission, drivetrain, fuel_type, exterior_color, condition.
  • Pagination: limit (max 100) and offset. Response includes count.

v1 API (X-API-Key)

For third-party apps. Create a key in Settings → API Keys. The key is scoped to one rooftop. Same since / updated_since as the public list; newest-first.

List inventory
curl -sS "https://app.flashfender.com/api/v1/inventory?limit=50&since=2026-08-01T00:00:00.000Z" \
  -H "X-API-Key: YOUR_API_KEY"

CORS allows X-API-Key. Rate limit is 100 requests/minute per key. Sending another dealer’s id in the path returns 403 — the key cannot read other tenants.

Incremental sync

Poll every N minutes with updated_since. Merge by id. Use since only when you care about brand-new listings, not price/photo edits.

Merge by id
const byId = new Map(); // persist across polls
let cursor = localStorage.getItem("ff_updated_since") || "1970-01-01T00:00:00.000Z";

async function poll() {
  const url = new URL("https://app.flashfender.com/api/vehicles/public");
  url.searchParams.set("token", "YOUR_EMBED_TOKEN");
  url.searchParams.set("updated_since", cursor);
  url.searchParams.set("limit", "100");

  const { data = [] } = await (await fetch(url, { credentials: "omit" })).json();
  for (const v of data) byId.set(v.id, v);

  for (const v of data) {
    if (v.updated_at && v.updated_at > cursor) cursor = v.updated_at;
  }
  localStorage.setItem("ff_updated_since", cursor);
}

Isolation: Drip’s token or API key only returns Drip (4d43b08c-3d56-4b3f-b465-c8dd5d50e62e). A second rooftop’s credentials never include those rows.

Widget source: https://app.flashfender.com/embed/inventory.js. Dealers: Settings → Website.