Skip to content

Forgeplane Coordinator API reference

Forgeplane exposes product resources under /api/v1. Choose the access path that matches the caller, then use the running coordinator’s responses as runtime evidence. Do not build automation by scraping the browser UI.

Access path Entry point Use it for Important boundary
LLM documentation index llms.txt A fetch-based agent discovering the official API, authentication, permissions, and workflow documentation It is a curated public-documentation index, not a live coordinator endpoint list or an authorization contract.
Interactive API documentation /api/docs A human operator or browser-capable agent browsing declared methods, parameters, request bodies, responses, and status codes on the running coordinator An agent may inspect it in read-only mode without a key. Only an authorized human should enter a personal key and use Try it out; the page keeps the key in that page only and clears it on reload.
Direct HTTP /api/v1 AI agents, scripts, CI jobs, and application integrations using curl or a standard language HTTP client Use JSON and an API key. Restrict the client to the intended host, methods, and routes.
Forgeplane web UI /catalog, /projects, /environments, /workloads/runs, and related pages Human resource selection, review, approval, and operations Uses a Forgeplane browser session. Treat it as a human interface, not a machine API contract.

For machine integrations, the normal workflow is:

  1. fetch Forgeplane llms.txt and follow only the official documentation links needed for the task;
  2. identify the target coordinator;
  3. let a browser-capable agent inspect /api/docs in read-only mode, or have an operator inspect it, for the declared operations and fields;
  4. apply the current behavior and permissions sections below;
  5. configure a narrow direct-HTTP allowlist;
  6. authenticate the integration through X-API-Key;
  7. verify the principal with GET /api/v1/auth/whoami;
  8. call only the reviewed /api/v1 operations; and
  9. preserve response status, body, and correlation headers.

A fetch-only agent should not scrape the JavaScript-rendered /api/docs page or the browser UI. A browser-capable agent can browse the interactive API documentation without credentials, but it must not use Authorize or Try it out unless an operator explicitly authorizes the exact call. Live resource browsing belongs on allowlisted direct GET /api/v1 requests.

These public endpoints identify the target before authentication:

Operation Purpose
GET /api/v1/system/identity Read the coordinator’s public system identity
GET /api/v1/system/version Read the coordinator API version contract

Use both when an integration can reach more than one Forgeplane installation. Stop if the returned target is not the reviewed installation or release.

A standard HTTP client such as curl can call the API directly:

Terminal window
curl --fail-with-body --silent --show-error \
"$FORGEPLANE_URL/api/v1/auth/whoami" \
-H "Accept: application/json" \
-H "X-API-Key: $FORGEPLANE_API_KEY"

Keep the key in the caller’s secret store. Do not put it in a prompt, repository file, generated report, command argument captured by an agent, or model-visible environment dump.

Option Transport Intended caller
User or service-account API key X-API-Key: <api-key> Non-browser automation
Forgeplane session JWT Authorization: Bearer <session-jwt> Operations that accept the Forgeplane bearer-session path
Forgeplane browser session session cookie Human use through the web UI

For durable automation, prefer a service account with the narrowest suitable role and owner scope. Do not reuse a human administrator’s browser cookie as an automation credential.

Authentication establishes identity only. Platform permissions, organization membership, team actions, resource ownership, lifecycle state, feature gates, quotas, and approval policy still apply.

  • 401 Unauthorized means authentication is missing, invalid, or expired.
  • 403 Forbidden means the principal is authenticated but an authorization or resource check failed.
  • A valid credential does not bypass team or resource boundaries.

See Authentication and Permissions and roles.

The table below shows representative access options. Open /api/docs on the target coordinator to browse the declared methods, fields, response shapes, and status codes for that release. Then apply the current behavior and permissions sections below, because the actual server state and authorization decision remain authoritative.

Resource area Read and discovery options Mutation or workflow options
Identity and authorization GET /api/v1/auth/whoami; POST /api/v1/auth/can-i Permission checks are preflight only; the real operation still performs authorization
Users and profiles GET /api/v1/users; GET /api/v1/users/{id}; browser profile at /profile user:manage governs user administration, including another user’s enabled state; profile:manage governs only the authenticated user’s own profile, including POST /profile/password
Organizations and teams /api/v1/organizations; /api/v1/organizations/{orgID}/teams; membership and invitation subresources Create, update, archive, transfer, invite, and membership operations subject to organization and team authority
Projects and environments GET /api/v1/projects; GET /api/v1/environments; read by ID Create, update, delete, and project transfer operations for authorized principals
Template registry GET /api/v1/registry/templates; template versions by template ID and semantic version Create or update entries, publish versions, validate variables, and trigger a governed run
Runs GET /api/v1/runs; GET /api/v1/runs/{id}; persisted logs at /logs Template-run admission, approval request/review, cancellation, requeue, and controlled deletion
Assemblies GET /api/v1/assemblies; versions, diffs, validation, assembly runs, nodes, and events Create or publish assemblies, start runs, approve a pending node, cancel, or resume
Drift GET /api/v1/drift-monitors; GET /api/v1/drift-findings; evidence by finding ID Monitor management, suppression, reopening, and gated remediation requests
Selective Undo Eligibility under /api/v1/runs/{run_id}/selective-undo; attempt reads under /api/v1/selective-undo/{attempt_id} Baseline checks, candidate checks, and explicit removal-change promotion
Service accounts and keys /api/v1/service-accounts; API-key subresources; /api/v1/profile/api-keys Create, disable, enable, delete, rotate, or revoke according to owner and role constraints
Plugin authority Installation action discovery; action grant-review and self-subject check under /api/v1/plugin-actions Explicit action assignments, installation ceilings and scoped background identities; no installation or administrator execution default
Connections and webhooks /api/v1/admin/connections; /api/v1/admin/webhooks Manage, test, rotate, or delete integrations with administrative authority
Audit and notifications /api/v1/audit; GET /api/v1/audit/actions; /api/v1/notifications; preferences and threads Query and export authorized audit data; discover the static audit action catalog; update notification state and preferences; administer channels when authorized
Workers and operations GET /api/v1/workers; system identity/version; health and readiness endpoints Quota, notification, and managed-state recovery operations are administrative and fail closed

This directory is intentionally grouped by resource instead of duplicating every operation. The interactive API documentation shows the declared options for the running coordinator. The current behavior sections and linked product guides explain runtime, lifecycle, authorization, and safety rules that route descriptions alone cannot provide.

Purpose Operation Client guidance
Read current state GET /api/v1/runs/{id} Preferred polling source for machine clients
Read persisted logs GET /api/v1/runs/{id}/logs Preferred log source for machine clients
Stream UI events GET /api/v1/runs/{id}/logs/stream SSE is UI-oriented; component events can contain HTML and no generic reconnect contract is documented

The pending_approval value is a valid nonterminal run status. Treat queued, pending_approval, running, and canceling as nonterminal; treat succeeded, failed, canceled, and timed_out as terminal. Fail closed on an unknown status.

On releases that declare the Plugin Permissions group in /api/docs, use the dedicated interfaces rather than inventing plugin tuples for auth/can-i:

Operation Purpose
GET /api/v1/plugin-installations/{id}/actions Discover declared actions for an authorized selector
POST /api/v1/plugin-actions/{id}/grant-review Review current grant-management authority and revisions
POST /api/v1/plugin-actions/{id}/check Check an action for the authenticated caller; no subject selection or admission token
POST /api/v1/plugin-action-grants; DELETE /api/v1/plugin-action-grants/{id} Explicitly assign or revoke one action for an eligible principal and selector
PUT /api/v1/plugin-installations/{id}/ceiling Replace the installation ceiling without granting subject authority
POST /api/v1/plugin-background-principals; DELETE /api/v1/plugin-background-principals/{id} Approve or revoke a scoped internal identity, not a public credential

Commands require exactly one canonical lowercase nonzero UUID Idempotency-Key and the declared expected revisions and complete ownership snapshot. Unused ownership components are explicit zero UUIDs/revisions, not omitted or null. Ceiling requests require both actions and executions; empty arrays grant nothing. Discover the exact fields in the target release instead of constructing ownership or authority from plugin claims. All responses are Cache-Control: no-store.

Grant/review/administration decisions use scoped membership-administration authority; ceiling replacement uses settings.manage. Platform administrators still need explicit action assignments and eligible scope to execute. A background identity starts with no assignments. See Plugin action permissions.

A committed command returns a minimal receipt with its actual revision/state and audit_record_id. Reconcile a lost response or unknown outcome through GET /api/v1/commands/outcome with the original key: not_observed is not rollback proof and does not authorize blind resubmission. See original-key outcomes.

Hard cutover: existing metadata-only releases need explicit staging of their original exact manifest before ceiling/execution administration. Executable ceilings pin the release and installation revision; activation or revision changes require fresh approval. Stale ceiling entries deny execution rather than becoming an implicit grant. These interfaces do not activate plugin processes, deliver protected inputs, schedule effects or provide the later configuration UI.

Current request and authorization behavior

Section titled “Current request and authorization behavior”

POST /api/v1/auth/can-i reviews only the authenticated User or service account using the following request envelope:

{
"resource_attributes": {
"group": "forgeplane.io",
"version": "v1",
"resource": "projects",
"verb": "get",
"name": "c72ec658-001b-45a3-9cb1-01958e9ac9f3",
"team_id": "47311af9-cce2-49e8-83b0-7263f7a34b66"
}
}

The envelope accepts only resource_attributes. Its only fields are group, version, resource, verb, name, and team_id. Group and version must be forgeplane.io and v1. Resource and verb must exactly match the catalog declared in the running coordinator’s API documentation.

  • Named reviews require name; supported collection reviews omit it. An allowed collection can be empty.
  • Team-scoped reviews require a nonzero UUID team_id. For named resources it must match the owning Team. Other scopes reject it.
  • settings is named global. Use service_accounts and drift_monitors, not dashed resource names. There is no aggregate workloads resource.
  • Named organizations reviews accept an Organization UUID, its current slug, or a historical slug. All forms resolve to the same Organization scope. An existing ID takes precedence over a matching slug. Only a missing ID falls back to slug lookup; an authorization denial does not.
  • Own-User get and update reviews use self-profile authorization. Another User’s ID selects administrative User authorization, not that User’s profile permission. Service accounts use their own grants, not their owner’s grants.
  • Unknown fields, duplicate keys, wildcards, selectors, namespaces, subresources, non-resource requests, subject selection, and impersonation return HTTP 400.

Valid reviews return HTTP 200 with a nested status:

Outcome allowed denied Detail
Allow true false reason.code: allowed
Deny false true reason.code: permission_denied, resource_not_found_or_denied, or principal_not_supported
Indeterminate false false evaluation_error.code: resolver_unavailable, authorization_unavailable, or evaluation_failed

reason and evaluation_error are separate {code, message} objects with safe summaries; exactly one is present. Missing, inaccessible, and wrong-Team named objects share the same denial. Internal system Principals receive principal_not_supported without resource resolution. Invalid authentication, including disabled Principals and revoked or expired credentials, returns 401. Operational failures while refreshing an authenticated principal before evaluation return 500, not an authentication denial. Malformed reviews return 400 with {"error":{"code":"…","message":"…"}}.

Treat an indeterminate result as unavailable authorization, not a definitive denial or permission to proceed. Read status.allowed and status.denied. Do not treat can-i as a reservation: the real operation rechecks authorization inside its mutation transaction and can still fail because scope, resource state, policy, quota, or approval state changed.

Each User API request is evaluated against the current principal and, when present, the target using service-side canonical authorization.

  • GET /api/v1/users and POST /api/v1/users require platform user:manage. profile:manage does not grant collection access.
  • A named GET for another user requires user:manage. A named GET for the authenticated user uses the identity-bound self policy and requires profile:manage.
  • A named PUT for another user requires user:manage. The authenticated user may use profile:manage to change only its own email and name; self role and enabled-state changes are forbidden. The optional enabled field is preserved when omitted and can be changed only for another user.
  • Self PUT requests must not include password. Change the password through the dedicated profile flow described in Authentication. Omitted user fields retain their current values, and password_hash is server-managed.
  • Passwords set through POST /api/v1/users or an administrator PUT must be at least 8 characters; shorter values return 400.
  • DELETE /api/v1/users/{id} requires user:manage; self-profile access does not grant deletion.

User responses include the required boolean enabled; an omitted enabled update preserves the current value.

New users are enabled by default. Disabling a user prevents sign-in and removes its effective platform assignments; current sessions and user-owned API credentials are rejected when current principal state is refreshed. Missing or inaccessible named users are returned as 403 Forbidden to preserve resource privacy.

The coordinator rejects 409 Conflict when a role, enabled-state, or deletion mutation would remove the final effective platform administrator. That count includes an enabled User or an enabled service account with a valid built-in admin assignment and owner relationship; API credential expiry or revocation does not remove the underlying assignment from the count. A service account is an independent Principal with its own role and enabled state. A User’s grants do not transfer to a User-owned service account, and disabling the owner does not disable the account.

User deletion returns 409 Conflict when the target owns service accounts, is the sole owner of an organization or team, or another administrator/ownership invariant would be broken. Reassign every User-owned service account and transfer ownership before retrying.

Retained historical references, including secret creators and invitation attribution, can also block deletion with 409 Conflict. The transaction rolls back without changing the User or its history. Disable the User when that history must remain rather than deleting historical records.

Notification preferences inherit current self-User read/update authorization and do not introduce separate can-i resource tuples.

GET /api/v1/admin/connections returns a metadata-only object. The connections array is always present, including when empty:

{
"connections": []
}

connections contains authorized metadata without plaintext credentials. List and detail reads do not decrypt credentials, so corrupt credentials do not remove otherwise readable records. The former errors array and credential_unavailable list code are removed: this is a direct contract change, not a partial-results compatibility path.

Discovery is not proof of credential usability. Protected execution and saved-settings checks resolve credentials under current authority and fail explicitly when resolution is unavailable. Unauthorized IDs remain private. Database, authorization-evaluation, and genuine metadata errors fail the whole request; a credential-codec configuration failure does not block metadata reads. Use the optional project_id parameter to filter by project. Results are not paginated. See Connections for the corresponding UI and environment-binding behavior.

Most JSON success responses return the resource or result directly. They do not use one universal top-level data envelope. Inspect the declared operation in /api/docs, apply the current behavior on this page, parse only fields your integration needs, and preserve unknown or changed behavior as an error until it is reviewed.

Coordinator errors generally use this shape:

{
"error": "message"
}

Common statuses are 400, 401, 403, 404, 409, 422, and 500. Some operations also return 202 for accepted asynchronous work, 413 for an oversized request, 429 for rate limiting, or 503 when a required service is unavailable. A 202 Accepted response means the coordinator accepted the operation; it does not mean the infrastructure action succeeded.

JSON handlers that use the coordinator’s strict decoder:

  • reject unknown fields;
  • require one JSON value rather than trailing JSON; and
  • cap the request body at 1 MiB.

Do not silently drop a field after the coordinator rejects it. Recheck the declared target operation in /api/docs, inspect the actual response, and correct the caller deliberately.

Responses can include:

  • X-Request-ID, the request correlation identifier;
  • X-Support-ID, the support-facing identifier; and
  • operation-specific response headers.

Preserve these headers when reporting an error. Clients may send X-Client-Request-ID to correlate their own logs, but that header does not make a request idempotent and does not replace the coordinator’s response identifiers.

Forgeplane does not define one retry policy for every mutation. Use an Idempotency-Key only for an operation that explicitly supports it. Do not automatically retry a mutation after an ambiguous timeout unless the reviewed operation makes that retry safe.

Organization, Team, Project and Environment deletion APIs require exactly one Idempotency-Key header containing a canonical nonzero lowercase UUID:

DELETE /api/v1/organizations/{orgID}
DELETE /api/v1/organizations/{orgID}/teams/{id}
DELETE /api/v1/projects/{id}
DELETE /api/v1/environments/{id}
Idempotency-Key: <original-command-uuid>

Missing, duplicate or noncanonical keys return 400. A key binds its original caller, command, target and accepted input; changing that binding conflicts. Existing authorization, dependency checks and cleanup still apply. A confirmed API deletion returns 204.

After a lost response or 503 with outcome: unknown, read the original result instead of submitting another mutation:

GET /api/v1/commands/outcome
Idempotency-Key: <the-same-original-command-uuid>

The response is kind: committed with a minimal recorded receipt, or kind: not_observed without a receipt. not_observed means only that this snapshot found no committed receipt; it is not rollback proof. Do not automatically retry, generate a replacement key, or infer completion from a missing resource. All command responses are Cache-Control: no-store. Keep keys out of URLs, logs and browser storage.

Outcome reads freshly validate the original principal and credential. That caller can read its recorded result after membership or owning Team/Organization deletion; another principal cannot. Historical results do not authorize new writes, restoration or access to current private resource data.

POST /api/v1/plugin-resources/{id}/restore uses the same command-key header and requires expected_revision plus the complete expected_ownership snapshot: organization_id, team_id, project_id, environment_id, team_revision, project_revision and environment_revision. Every field is required and non-null. Unused ownership components use the zero UUID and revision 0, not omitted fields. The values are core registry facts, not plugin claims of authority.

Restoration requires current scoped Organization/Team membership-administration authority, the original eligible parent, compatible published declarations and intact declared SQL roots. Missing roots conflict; unavailable root verification is a failure, not permission to repair. A successful response returns the same resource UUID with advanced registry revision and eligibility generation.

Restoration does not recreate data, reparent a resource, grant execution access, activate plugin code or resume work. Compatible dormant installations can retain restored identities without becoming executable. Reconcile an unknown restoration outcome with the original key just as for parent deletion.

Selective Undo’s baseline check is an explicit example:

POST /api/v1/runs/{run_id}/selective-undo/check
Idempotency-Key: <stable-request-key>

Reuse the same stable key for retries of the same logical check. Other mutations can use conflict detection, lineage checks, optimistic concurrency, or no idempotency key. Review the target operation before adding retry behavior.

Operational endpoints are registered outside /api/v1. Their network exposure depends on how the coordinator and admin services are deployed.

Path Meaning
/health, /healthz Process liveness
/readyz Readiness, including required coordinator dependencies
/readyz/internal More detailed internal readiness information
/metrics Prometheus metrics when enabled; bearer protection can be configured

Keep /readyz/internal, /metrics, and the dedicated admin listener on trusted networks unless you have an explicit exposure policy. A healthy liveness response does not prove that the coordinator is ready to execute infrastructure work.