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.
Choose an access path
Section titled “Choose an access path”| 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:
- fetch Forgeplane llms.txt and follow only the official documentation links needed for the task;
- identify the target coordinator;
- let a browser-capable agent inspect
/api/docsin read-only mode, or have an operator inspect it, for the declared operations and fields; - apply the current behavior and permissions sections below;
- configure a narrow direct-HTTP allowlist;
- authenticate the integration through
X-API-Key; - verify the principal with
GET /api/v1/auth/whoami; - call only the reviewed
/api/v1operations; and - 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.
Identify the running coordinator
Section titled “Identify the running coordinator”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.
Direct HTTP quick start
Section titled “Direct HTTP quick start”A standard HTTP client such as curl can call the API directly:
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.
Authentication options
Section titled “Authentication options”| 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 Unauthorizedmeans authentication is missing, invalid, or expired.403 Forbiddenmeans 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.
Resource directory
Section titled “Resource directory”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.
Run observation options
Section titled “Run observation options”| 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.
Plugin permission commands
Section titled “Plugin permission commands”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. settingsis namedglobal. Useservice_accountsanddrift_monitors, not dashed resource names. There is no aggregateworkloadsresource.- Named
organizationsreviews 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
getandupdatereviews 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.
User and profile operations
Section titled “User and profile operations”Each User API request is evaluated against the current principal and, when present, the target using service-side canonical authorization.
GET /api/v1/usersandPOST /api/v1/usersrequire platformuser:manage.profile:managedoes not grant collection access.- A named
GETfor another user requiresuser:manage. A namedGETfor the authenticated user uses the identity-bound self policy and requiresprofile:manage. - A named
PUTfor another user requiresuser:manage. The authenticated user may useprofile:manageto change only its own email and name; self role and enabled-state changes are forbidden. The optionalenabledfield is preserved when omitted and can be changed only for another user. - Self
PUTrequests must not includepassword. Change the password through the dedicated profile flow described in Authentication. Omitted user fields retain their current values, andpassword_hashis server-managed. - Passwords set through
POST /api/v1/usersor an administratorPUTmust be at least 8 characters; shorter values return400. DELETE /api/v1/users/{id}requiresuser: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.
Connection list results
Section titled “Connection list results”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.
Requests and responses
Section titled “Requests and responses”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.
Correlation and error handling
Section titled “Correlation and error handling”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.
Idempotency and retries
Section titled “Idempotency and retries”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.
Parent deletion and original-key outcomes
Section titled “Parent deletion and original-key outcomes”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/outcomeIdempotency-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.
Plugin resource restoration
Section titled “Plugin resource restoration”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 checks
Section titled “Selective Undo checks”Selective Undo’s baseline check is an explicit example:
POST /api/v1/runs/{run_id}/selective-undo/checkIdempotency-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.
Health and operational endpoints
Section titled “Health and operational endpoints”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.