Formula Keeper
DocsAPISign in

API docs

Developer UX

Formula Keeper now ships a versioned `v1` preview API for core records. This surface now accepts either workspace session auth or scoped bearer API keys while deeper external docs maturity remains the next step.

Launch positionAuth modelCurrent routesExamplesErrors`/v1` roadmap

Launch position

A versioned `v1` preview API is now part of the launch surface.

The current `v1` routes cover read-only access for core entities and use the same pagination and sorting envelope across list endpoints. The main remaining boundary is documentation depth and compatibility rigor, not bearer-auth availability.

{
  "version": "v1",
  "workspaceId": "ws_123",
  "meta": {
    "page": 1,
    "pageSize": 25
  },
  "data": []
}

Current auth posture

Public `v1` routes accept either normal Formula Keeper workspace authentication or a scoped bearer API key issued from workspace settings. Internal-only routes continue to use workspace auth or internal runner tokens as appropriate.

Current rate limits are `120` requests per minute for API keys and `180` requests per minute for workspace-session consumers.

Scope model

API keys now support scope selection, revoke, and rotation. Each current `v1` resource family maps to one read-only scope so external consumers can stay narrow.

Session auth in the browser

Use your normal Formula Keeper session cookie when calling the API from an authenticated workspace context.

fetch("/api/v1/raw-materials?page=1&pageSize=25", {
  credentials: "include"
})

Bearer auth from an external client

Issue a scoped key from Settings -> API Keys, then send it as a bearer token.

curl -X GET "$FORMULA_KEEPER_URL/api/v1/formulas?page=1&pageSize=25" \
  -H "Authorization: Bearer $FORMULA_KEEPER_API_KEY"

Current routes

Versioned preview plus internal support routes

These are real routes in the product today. The versioned `v1` surface is the public contract baseline; the internal routes remain app-supporting operational endpoints.

MethodPathAuthPurpose
GET/api/v1/raw-materialsSession or API keyVersioned raw-material list endpoint with shared pagination and sorting
GET/api/v1/formulasSession or API keyVersioned formula list endpoint with shared pagination and sorting
GET/api/v1/batchesSession or API keyVersioned batch list endpoint with shared pagination and sorting
GET/api/v1/vendorsSession or API keyVersioned vendor list endpoint with shared pagination and sorting
MethodPathAuthPurpose
GET/api/healthNoneInternal liveness and database connectivity check
POST/api/internal/background-jobs/run-dueBearer tokenInternal background-job runner entry point
GET/api/global-searchWorkspace authAuthenticated cross-record search inside the app shell
GET/api/raw-materials/exportWorkspace authCSV export for the raw-material workbench
GET/api/settings/audit-logWorkspace authFiltered audit feed with entity and actor metadata

Endpoint reference

Concrete request and response examples for the current `v1` surface

The current API is intentionally read-only. These examples use realistic workspace payload shapes so teams can integrate without reverse-engineering the app UI.

Raw materials

Required scope: RAW_MATERIALS_READ

Read-only v1 preview

List request

/api/v1/raw-materials?q=bergamot&page=1&pageSize=25&sort=updatedAt&direction=desc

Detail request

/api/v1/raw-materials/rm_123

Example response

{
  "requestId": "req_123",
  "version": "v1",
  "workspaceId": "ws_123",
  "data": [
    {
      "id": "rm_123",
      "name": "Bergamot FCF",
      "code": "RM-104",
      "supplier": "Aromatic Supply Co.",
      "regulatoryStatus": "REVIEWED",
      "unitLabel": "g",
      "updatedAt": "2026-04-02T15:22:00.000Z"
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 25,
    "totalCount": 1,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPreviousPage": false,
    "query": "bergamot",
    "sort": "updatedAt",
    "direction": "desc"
  }
}

Formulas

Required scope: FORMULAS_READ

Read-only v1 preview

List request

/api/v1/formulas?q=cologne&sort=version&direction=desc

Detail request

/api/v1/formulas/fo_123

Example response

{
  "requestId": "req_123",
  "version": "v1",
  "workspaceId": "ws_123",
  "data": {
    "formula": {
      "id": "fo_123",
      "name": "Citrus Cologne",
      "version": 3,
      "revisionMinor": 1,
      "status": "APPROVED"
    },
    "batchUsage": [],
    "versionFamily": [],
    "categoryColor": "#64748B"
  }
}

Batches

Required scope: BATCHES_READ

Read-only v1 preview

List request

/api/v1/batches?q=pilot&sort=updatedAt&direction=desc

Detail request

/api/v1/batches/ba_123

Example response

{
  "requestId": "req_123",
  "version": "v1",
  "workspaceId": "ws_123",
  "data": {
    "batch": {
      "id": "ba_123",
      "name": "Pilot Batch 03",
      "status": "QUALITY_CONTROL",
      "formulaName": "Citrus Cologne",
      "totalOutputGrams": 1200
    }
  }
}

Vendors

Required scope: VENDORS_READ

Read-only v1 preview

List request

/api/v1/vendors?q=aromatic&sort=name&direction=asc

Detail request

/api/v1/vendors/ve_123

Example response

{
  "requestId": "req_123",
  "version": "v1",
  "workspaceId": "ws_123",
  "data": {
    "vendor": {
      "id": "ve_123",
      "name": "Aromatic Supply Co.",
      "email": "purchasing@example.com",
      "isActive": true,
      "contacts": [],
      "addresses": []
    }
  }
}

Error model

Stable error codes for the current launch surface

CodeHTTPMeaning
UNAUTHORIZED401No valid workspace session or bearer API key was supplied.
FORBIDDEN403The current role or API key scope does not have access to the requested route.
NOT_FOUND404The requested record does not exist in the active workspace.
INVALID_SORT_FIELD / INVALID_PAGE / INVALID_PAGE_SIZE400The list query parameters do not match the documented v1 contract.
RATE_LIMITED429The current API key or workspace session exceeded the current per-minute request limit.
INTERNAL_ERROR500The route failed unexpectedly. Use the returned request ID when investigating logs.

Retry guidance

How external consumers should behave

Do not retry `400`, `401`, `403`, or `404` responses until the request shape, auth, scope, or record ID is corrected.
Treat `429 RATE_LIMITED` as a real throttle. Respect `Retry-After` and the `X-RateLimit-*` headers before retrying.
Retry `500` responses with bounded backoff and log the returned `requestId` for support investigation.
Rotate keys immediately after exposure risk. Rotation revokes the previous secret in one step.
Keep integrations read-only for now. The current `v1` preview is meant for visibility and sync, not remote mutation.

Roadmap

What still needs to mature after the `v1` baseline

Richer request and response examples per route
Explicit error and retry guidance for external consumers
Backward-compatibility policy with changelog discipline

Related docs

Use the public docs surface to orient teams before they enter the app.

Docs overviewOpen workspace