Skip to main content
Private preview. fremforge is in private preview — invited customers only. Content is still subject to change. Request access →
Public REST API

Public REST API

Every fremforge admin-UI action has a public REST API equivalent. The admin UI is itself a client of this surface, and AI agents acting on behalf of users consume the same endpoints. There are no admin-only endpoints and no surprise gaps.

Get the spec

The canonical machine-readable specification is OpenAPI 3.1.

  • YAML: /api/openapi.yaml, canonical, single-file, downloadable for tooling.
  • JSON mirror: published at /api/openapi.json at launch from the same source, for clients that prefer JSON.

Both files are versioned with the API and update on every fremforge release. The OpenAPI version field tracks MAJOR.MINOR.PATCH; the API itself is versioned in the path (/api/v1/...) for breaking changes.

Authentication

Three authentication paths, declared in the spec as separate securitySchemes so consumers can pick the right one.

Personal access tokens (PAT), for human callers and CI integrations

Personal access tokens are user-scoped, not org-scoped, so they live under your user settings on Forgejo: navigate to /<your-user>/settings/applications on the Forgejo UI (e.g. frem.sh/<your-user>/settings/applications). Tokens carry a name, a scope set, and a TTL up to 365 days. The raw token is shown once; only its hash is stored server-side.

Distinct from org API tokens (covered below under Token management) which live in fremforge admin at /<org>/_admin/api-tokens and survive individual member departures. Use a PAT for personal scripts and local development; use an org API token for CI and production automation.

curl -H "Authorization: Bearer ${FREMFORGE_PAT}" \
     https://frem.sh/_app/api/v1/users/me

OAuth 2.0 client credentials, for machine-to-machine

Standard RFC 6749 client-credentials grant. Register a client at the org level, exchange client_id + client_secret at the token endpoint:

curl -X POST https://frem.sh/_app/api/v1/auth/oauth/token \
  -d "grant_type=client_credentials" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=org:read seats:write"

OIDC token exchange, for IdPs and workflow runners

When your IdP can mint OIDC JWTs (Authentik, Entra, Okta, Keycloak, …) or when a fremforge Forgejo Actions workflow needs to call back into the API, prefer POST /api/v1/auth/token-exchange over long-lived PATs. No shared secret leaves your boundary, the IdP rotates the underlying key on its own schedule.

Accepted issuers:

  • Customer IdP, registered as an auth_sources entry for the target tenant (the same JWKS already used for member sign-in). Wire your IdP from the tenant SSO settings page first.
  • fremforge runner-OIDC, Forgejo Actions runner steps can self-mint a fremforge token without any extra IdP wiring. The issuer URL is published in the runner-job environment as FREMFORGE_OIDC_ISSUER.

Submitted JWTs MUST carry aud=fremforge-api and a recent exp. The returned access token is an ordinary ffp_ PAT, pass it on subsequent calls as Authorization: Bearer <token>. TTL is hard-capped at 1 hour. Scopes are the intersection of the request and the exchange allow-list; * and tokens:manage are never grantable here, for those, use POST /api/v1/auth/tokens or the admin UI.

# 1. Mint a JWT from your IdP (aud=fremforge-api).
ASSERTION="$(your-idp-cli get-token --audience fremforge-api)"

# 2. Exchange it for a short-lived fremforge access token.
ACCESS_TOKEN="$(curl -s -X POST \
  -H 'Content-Type: application/json' \
  -d "{\"subject_token\":\"$ASSERTION\",\"tenant_slug\":\"acme\",\"scopes\":[\"exports:write\",\"exports:read\"]}" \
  https://frem.sh/_app/api/v1/auth/token-exchange | jq -r .access_token)"

# 3. Drive the API with the resulting token (TTL = 1 hour).
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  https://frem.sh/_app/api/v1/users/me

From a Forgejo Actions workflow step the same flow looks like:

- name: Exchange runner OIDC for fremforge token
  run: |
    ASSERTION="$FREMFORGE_OIDC_TOKEN"  # injected by the runner controller
    echo "FREMFORGE_TOKEN=$(curl -sX POST \
      -H 'Content-Type: application/json' \
      -d "{\"subject_token\":\"$ASSERTION\",\"tenant_slug\":\"${{ github.repository_owner }}\",\"scopes\":[\"sboms:write\"]}" \
      https://frem.sh/_app/api/v1/auth/token-exchange | jq -r .access_token)" >> $GITHUB_ENV

The endpoint is unauthenticated by design (the JWT is the auth) but rate-limited at 30 attempts/minute per source IP. Bursts beyond that return 429 Retry-After. Each rejected exchange still costs an RSA verify, so a leaking IdP credential or a misbehaving CI loop is contained rather than amplifying.

Agent-identity OAuth (Phase 2+), for AI agents acting on behalf of users

The OpenAPI spec uses the x-fremforge-phase extension to mark endpoint availability:

  • x-fremforge-phase: 1, live and available now.
  • x-fremforge-phase: 2, specified but not yet implemented; returns 501 Not Implemented.

Endpoints without the extension are Phase 1. Attempting a Phase 2 endpoint returns {"error":"not_implemented","phase":2}.

The agent authenticates as the agent, on behalf of a named user, under a scoped delegated mandate the user has issued. RFC 8693 token-exchange shape with an on-behalf-of assertion. Audit-log records actor=agent:<agent_id>, on_behalf_of=<user_id> so agent actions are visibly distinct from human actions.

This flow is not yet live at launch. The endpoints are documented in the spec ahead of implementation so the admin UI and the monolith can be built against the final shape. See the AI posture page for the full roadmap.

Agent usage patterns

Three reference scenarios the spec was designed around. If your agent needs something the spec does not support cleanly, that is a reportable gap, tell us at support@frem.sh.

Scenario 1, Provisioning a new org from a delegated mandate

An agent acting on behalf of a reseller or admin mandate can provision a complete org in four API calls:

  1. POST /orgs, create the org with slug, display name, and billing contact
  2. PUT /orgs/{org}/sso, configure OIDC or SAML SSO for the new org
  3. POST /orgs/{org}/seats, provision the initial seat count
  4. GET /orgs/{org}, verify the org is active and SSO is configured

Scenario 2, Reading findings and creating remediation PRs

A read-scoped mandate. The agent polls the findings endpoints, identifies high-severity items, and opens remediation PRs through the standard Forgejo Git interface (out of scope for this REST API; the agent uses Git over HTTPS or SSH for that part). Audit-log entries appear with the distinctive agent-actor marker.

Scenario 3, Adjusting runtime policy during an incident

Tightly-scoped, time-boxed mandate (policy:write:scope=findings-only, valid 1h). The agent applies stricter policy (e.g., merge-block on CRITICAL CVEs across all repos), the change is audit-logged with the agent’s identity and the on-behalf-of user, and org owners see the change in the audit-stream filter.

Versioning policy

  • Major versions in the path (/v1/..., /v2/...). Breaking changes ship in a new major version with a minimum 12-month overlap during which both versions are supported.
  • Minor and patch versions in info.version. Additive endpoints, new optional fields, and documentation improvements land within the current major version without breaking compatibility.
  • The OpenAPI spec is the source of truth; if a behaviour differs from the spec, the spec is correct and the implementation will be patched. Report deviations to support@frem.sh.

Rate limits

API requests are rate-limited per token:

Request typeLimit
Read (GET)600 requests/minute
Write (POST, PUT, PATCH, DELETE)60 requests/minute

Exceeding a limit returns HTTP 429 with a Retry-After header indicating when the window resets. Limits apply per token, not per IP. Org API tokens and PATs share the same per-token limits.

Every response also carries IETF RateLimit and RateLimit-Policy headers per draft-ietf-httpapi-ratelimit-headers. Per-token rate limits and per-org rate limits both apply; the more restrictive of the two is reported in the response headers.

Validating the spec locally

Download the spec, then run a linter against the local file:

curl -fsSL https://docs.frem.sh/api/openapi.yaml -o openapi.yaml

# Redocly CLI
npx @redocly/cli@latest lint openapi.yaml

# Swagger CLI
npx @apidevtools/swagger-cli@latest validate openapi.yaml

The spec validates clean against both linters as a Phase 1 launch-blocker commitment.

What this spec does not cover

Two surfaces are intentionally out of scope:

  1. Forgejo-native API, endpoints under /api/v1/repos, /api/v1/users, /api/v1/issues, /api/v1/pulls, etc. Those are the Forgejo/Gitea API surface that fremforge exposes unmodified at https://frem.sh/_app/api/v1/.... Each running fremforge instance also serves the live Swagger UI at https://frem.sh/api/swagger (rendered straight from the running Forgejo, so it’s always in sync with the deployed version). The migration guides reference these endpoints directly because they are the standard Forgejo surface.
  2. Webhook delivery payloads, Forgejo’s webhook event shapes are upstream-defined. The fremforge admin UI lets you configure webhooks; payloads use the Forgejo event vocabulary (X-Forgejo-Event header, X-Forgejo-Signature HMAC-SHA256).

The OpenAPI spec covers the fremforge-specific control plane: organisations, seats, policy, findings, runners, exports, audit, mandates. Anything that exists because fremforge added it on top of Forgejo lives in this spec.

Token management

Two token types are available.

Token types

TypeTied toWhen to use
Org API tokenThe org, not a userCI systems, automation, and integrations that act on behalf of the org. Survives individual member departures.
Personal access token (PAT)An individual user accountLocal development, personal scripts, and cases where the caller identity should reflect a specific person in the audit log.

Rule of thumb: use an org API token for anything that runs in production or CI. Use a PAT for personal tooling and development workflows.

Creating an org API token

  1. Go to Org admin → API tokens → Mint a new token (frem.sh/<org>/_admin/api-tokens).
  2. Enter a descriptive name (e.g. ci-deploy, renovate-bot).
  3. Select scopes. Available scopes:
ScopeAccess granted
read:orgRead org metadata, membership list, settings
write:orgModify org settings and membership
read:repoClone and read all org repositories
write:repoPush, create branches, manage repository settings
read:audit-logRead the org audit log
write:secretsCreate and update org and repo secrets
read:packagesPull packages from the org package registry
write:packagesPush packages to the org package registry
  1. Set an expiry. Default TTL (when ttl_days is omitted) is 90 days. Maximum TTL is 365 days. Users can specify any value from 1 to 365 in the ttl_days field at creation time. Tokens cannot be set to never expire.
  2. Click Generate token.

The token value is shown exactly once at creation. Copy and store it immediately in your secret manager. fremforge does not store the raw value; only its hash is retained server-side. If you lose the value, revoke the token and create a new one.

Creating a PAT

  1. Go to User settings → Applications → Generate new token (frem.sh/user/settings/applications).
  2. Enter a name, select scopes, and set an expiry (default 90 days; maximum 365 days).
  3. Click Generate token.

PATs use the same scope model as org API tokens. A PAT inherits the access of the user who created it, further constrained by the selected scopes.

Token isolation

Org API tokens are strictly org-scoped. A token created under org acme cannot access any resource in org beta, even if the user who created the token is a member of both orgs. This is a platform-enforced boundary, not a permission check that can be bypassed.

This means you need one org API token per org for any multi-org automation. There is no cross-org token type.

Rotating a token

Rotate tokens when they are approaching expiry, when a team member who knew the value leaves, or when a token may have been exposed.

Rotation procedure:

  1. Create the new token at Org admin → Settings → Applications → New token.
  2. Update every consumer of the old token: CI secrets, deployment configs, .env.deploy.local files, third-party integrations.
  3. Verify the new token works end-to-end by triggering a test run or making a test API call.
  4. Revoke the old token at Org admin → Settings → Applications → find the token → Revoke.

Create first, update consumers, then revoke. Never revoke before the replacement is in place.

Revoking a token

  1. Go to Org admin → Settings → Applications (for org API tokens) or User settings → Applications (for PATs).
  2. Find the token by name.
  3. Click Revoke.

Revocation is immediate. Any request using the revoked token receives a 401 Unauthorized response. There is no grace period.

Listing active tokens

Org admin → Settings → Applications shows all org API tokens with:

  • Token name
  • Creator username
  • Creation date
  • Last-used date
  • Expiry date
  • Scopes

Token values are not visible after creation. Org owners can see all token metadata regardless of which owner created the token. Only the token creator saw the value at creation time.

For PATs, each user can list their own tokens at User settings → Applications. Org owners cannot enumerate another user’s PATs, but a removed member’s PATs automatically lose access to org resources the moment that member is removed from the org.

Token expiry

Tokens expire automatically at their configured TTL. An expired token returns 401 Unauthorized. Expired tokens cannot be renewed. Create a new token and update consumers; then optionally delete the expired token from the list.

To avoid outages from silent expiry, set an alert in your monitoring system based on the expiry date shown in the token list, or rely on the notification email fremforge sends 7 days before an org API token expires (sent to org owners).

Security practices

  • Store tokens as encrypted CI secrets, not in repository files, workflow YAML, or commit history.
  • Use the minimum scope set needed for the task. A token that only reads repos does not need write:org.
  • Set the shortest TTL that is operationally practical. The default is 90 days; 365 days is the maximum.
  • Rotate tokens that may have been exposed rather than assuming they were not used.
  • Audit token last-used dates periodically. A token that has not been used in 30 days may be a stale credential from a decommissioned integration.

Get help

API behaviour questions, missing endpoints, or proposals for new operations: support@frem.sh with subject line starting API -.