The FerrFlow API exposes a small set of hosted HTTP endpoints under https://api.ferrlabs.com/v1/ferrflow/*. They are backed by the same FerrFlow core the CLI runs, so validate and preview return results identical to ferrflow validate and ferrflow check — no second implementation to drift.

Every endpoint is public (no authentication) and safe to call from CI, editors, or a browser. The machine-readable contract is served at /v1/ferrflow/openapi.json (OpenAPI 3.1).

GET /v1/ferrflow/health

Liveness and version probe. Powers status dashboards.

{ "status": "ok", "service": "ferrflow-api", "version": "10.17.0", "time": "2026-07-21T15:00:00Z" }

GET /v1/ferrflow/schema

Returns the config JSON Schema (Content-Type: application/schema+json), served from the schema bundled in the FerrFlow release — the same bytes the CLI validates against. Sends a strong ETag and Cache-Control, so point your editor's $schema here:

{ "$schema": "https://api.ferrlabs.com/v1/ferrflow/schema" }

GET /v1/ferrflow/schema/v{major} returns the schema frozen at a CLI major (e.g. /schema/v5). Only the current major is served today; older majors return 404 until per-major snapshots land.

GET /v1/ferrflow/latest

Resolves the latest FerrFlow release from GitHub, cached server-side. Pass platform to get a single asset:

curl "https://api.ferrlabs.com/v1/ferrflow/latest?platform=linux-x64"
{
  "version": "5.45.0",
  "tag": "v5.45.0",
  "platform": "linux-x64",
  "download_url": "https://github.com/FerrLabs/FerrFlow/releases/download/v5.45.0/ferrflow-linux-x64.tar.gz",
  "signature_url": "https://github.com/FerrLabs/FerrFlow/releases/download/v5.45.0/ferrflow-linux-x64.tar.gz.sig",
  "cert_url": "https://github.com/FerrLabs/FerrFlow/releases/download/v5.45.0/ferrflow-linux-x64.tar.gz.crt",
  "published_at": "2026-07-21T13:24:24Z"
}

Releases are signed with Sigstore — verify the .sig + .crt rather than a checksum. Without platform, the response lists assets for every platform. Valid platforms: linux-x64, linux-arm64, linux-arm, darwin-x64, darwin-arm64, win32-x64, win32-arm64.

POST /v1/ferrflow/validate

Validates a config without a repo — you send the config text and, optionally, the contents of the versioned files it references so the file-existence and version-consistency checks run. The result is identical to ferrflow validate --json.

curl -X POST https://api.ferrlabs.com/v1/ferrflow/validate \
  -H 'content-type: application/json' \
  -d '{
    "config": "{\"package\":[{\"name\":\"app\",\"path\":\".\",\"versionedFiles\":[{\"path\":\"package.json\",\"format\":\"json\"}]}]}",
    "files": { "package.json": "{\"version\":\"1.0.0\"}" }
  }'
{
  "valid": true,
  "config_file": null,
  "package_count": 1,
  "errors": [],
  "warnings": [],
  "suggestions": []
}

An invalid config is still a successful validation: the response is 200 with "valid": false and the offending entries. Only a malformed request body returns 400. The optional format field (json | json5 | toml) skips format inference.

POST /v1/ferrflow/preview

Computes the version bumps and changelog for an explicit list of commits — the same logic as ferrflow check, as a service. No repo access; you pass the commits.

curl -X POST https://api.ferrlabs.com/v1/ferrflow/preview \
  -H 'content-type: application/json' \
  -d '{
    "config": "{\"package\":[{\"name\":\"api\",\"path\":\".\"}]}",
    "commits": [{ "message": "feat(api): add endpoint", "hash": "a1b2" }],
    "current_versions": { "api": "1.2.3" }
  }'
{
  "packages": [
    {
      "name": "api",
      "current": "1.2.3",
      "next": "1.3.0",
      "bump": "minor",
      "commits": [{ "hash": "a1b2", "type": "feat", "scope": "api", "breaking": false }],
      "changelog": "### Features\n- ..."
    }
  ]
}

In a monorepo config, each commit is assigned to a package when its files fall under that package's path. Packages with no releasable commit are omitted.