Firmware API

A public, unauthenticated HTTP API listing every published firmware release and its cryptographic hashes. Use it to confirm a board is running official, competition-legal firmware.

Basics

Base URLhttps://openfc.dev/api/v1
AuthNone. Public and read-only.
FormatJSON. Every response includes api_version and generated_at.
CORSEnabled for all origins.
Rate limitsNone. Static files behind a CDN.

The API is regenerated from the firmware manifest on every deploy, and the build fails if a published hash disagrees with the binary actually being served. A response can therefore never advertise a hash for a file that is not the file you would download.

api_version only changes on a breaking change. New fields may be added within v1, so parse leniently and ignore unknown keys.

Endpoints

EndpointReturns
GET /api/v1/index.jsonService description and the endpoint list.
GET /api/v1/firmware/latest.jsonThe newest competition-certified release.
GET /api/v1/firmware/releases.jsonEvery published release, newest first.
GET /api/v1/firmware/hashes.jsonSHA-256 → release lookup, for verifying a connected board.

Latest certified firmware

Answers “what should this board be running?” latest is null if nothing is currently certified.

GET /api/v1/firmware/latest.json
{
  "api_version": "v1",
  "generated_at": "2026-07-31T01:39:53.093Z",
  "latest": {
    "version": "1.3.0",
    "released": "2026-07-31",
    "description": "Adds get_info and closes the raw-diagnostics watchdog gap",
    "changelog": "...",
    "binary_sha256": "0403cd4246c29d8e524b9f0cbe9300ea3fb399366996a1a8f8b61efd071b8887",
    "app_sha256": "0217fc0dbc1acb26c4e7ae9edbd3833a87ff8e530a83655e0d52db4a006fa3f7",
    "download_url": "https://openfc.dev/firmware/openfc-v1.3.0-merged.bin",
    "certified_for_competition": true,
    "min_hw_version": "1.1"
  }
}

Two hashes, and which one to use

Each release publishes two SHA-256 values. Using the wrong one is the most common integration mistake, because both are valid hashes of “the firmware” — of different things.

FieldHash ofCompare against
app_sha256The application partition imageThe fw_hash a connected board reports in hello.
binary_sha256The merged .bin file you downloadA file on disk, before flashing it.
To verify a connected board, use app_sha256. To verify a downloaded file, use binary_sha256. The lookup table below accepts either and tells you which one matched.

Verifying a board for competition

The board computes the SHA-256 of its own running application partition at boot and reports it as fw_hash. That is a hash of the code actually executing, not a version string a modified build could simply claim — so it is meaningful evidence rather than a self-declaration.

Three steps:

  1. Send {"cmd":"get_info"} and read fw_hash from the hello reply.
  2. Fetch /api/v1/firmware/hashes.json.
  3. Look up the hash. It is competition-legal only when the entry exists and certified_for_competition is true.
GET /api/v1/firmware/hashes.json
{
  "api_version": "v1",
  "generated_at": "2026-07-31T01:39:53.093Z",
  "hashes": {
    "0217fc0dbc1acb26c4e7ae9edbd3833a87ff8e530a83655e0d52db4a006fa3f7": {
      "version": "1.3.0", "certified_for_competition": true, "kind": "app"
    },
    "0403cd4246c29d8e524b9f0cbe9300ea3fb399366996a1a8f8b61efd071b8887": {
      "version": "1.3.0", "certified_for_competition": true, "kind": "binary"
    }
  }
}
node — inspection check
const hashes = await fetch(
  'https://openfc.dev/api/v1/firmware/hashes.json'
).then((r) => r.json());

const entry = hashes.hashes[fwHash.toLowerCase()];

if (!entry)                                console.log('FAIL — unknown firmware');
else if (!entry.certified_for_competition) console.log('FAIL — uncertified build');
else                                       console.log('PASS —', entry.version);
A hash that is absent from the table is not necessarily malicious — it is most often a self-built or development firmware. It does mean the board is not running a build we published, so it cannot be treated as certified.

TypeScript client

This site's own client is importable if you are working in TypeScript. It wraps the same endpoints and returns a single verdict.

lib/openfc-api.ts
import { verifyFirmware } from '@/lib/openfc-api';

const verdict = await verifyFirmware(fwHash, 'https://openfc.dev');

verdict.certified     // legal for competition
verdict.recognised    // a published build, certified or not
verdict.version       // "1.3.0"
verdict.outdated      // a newer certified release exists
verdict.latestVersion // "1.3.0"

verdictFor() is the same logic as a pure function, if you want to cache the index and check many boards offline.

Checking for updates

is this board up to date?
const { latest } = await fetch(
  'https://openfc.dev/api/v1/firmware/latest.json'
).then((r) => r.json());

if (latest && fwHash.toLowerCase() !== latest.app_sha256) {
  console.log('Update available:', latest.version, '→', latest.download_url);
}

Compare hashes rather than version strings: the hash distinguishes a genuine release from a local build that reports the same version number.

Stability

Endpoint paths and existing field names within v1 will not change or be removed. A published release is never edited or deleted — if a build is withdrawn, certified_for_competition flips to false and the entry stays, so an old hash always resolves to a truthful answer rather than disappearing.