The site manifest
A manifest is one document that says what your organization should have — spaces, their theme and shape, redirects, collections, forum categories, status monitors, and the AI knowledge sources behind Ask. You keep it in your own repository, and applying it makes the platform match it.
It exists so that standing up a site, or rebuilding one, is a file you can review rather than a sequence of clicks nobody wrote down.
The shape of it
{
"version": 1,
"spaces": [
{
"name": "Docs",
"slug": "docs",
"type": "DOCS",
"visibility": "PUBLIC",
"preset": "landing",
"shape": { "index": "landing", "nav": "tree", "whoCreates": "staff" },
"theme": { "primaryColor": "#4f39e6" },
"redirects": [{ "from": "/old-intro", "to": "/getting-started" }],
"collections": [{ "name": "Guides", "slug": "guides" }]
}
],
"sources": []
}The vocabulary for type, and which blocks a given type may declare, come from GET /space-types — the same declaration the console renders. A manifest can never describe a kind of space the product does not have.
Applying it
PUT /orgs/{orgId}/manifest # a DRY RUN. Nothing is written.
PUT /orgs/{orgId}/manifest?apply=true # do itA dry run is the default, not a flag you remember. The response is a per-unit diff against what is there today, so you read what would change before anything does.
curl -X PUT "https://forumulate.com/api/orgs/$ORG/manifest" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
--data @site.jsonFour properties worth relying on:
It is idempotent. Applying the same manifest twice does the work once; the second run answers
unchangedfor every unit.It is additive. Anything that exists and the manifest does not mention is reported under
extrasand is never deleted. A manifest is not a destructive sync, and there is no flag to make it one.It is not one transaction. Each subsystem opens its own — that is where tenant isolation lives — so an apply is a sequence of idempotent units that stops at the first failure and names where it stopped. A partial apply is resumed by fixing the manifest and applying again, which is safe precisely because of the first two properties.
It cannot grant you anything. Each unit re-checks the capability of the endpoint that owns it, and a dry run is refused wherever the apply would be. You cannot reach through a manifest to do something your token could not do directly.
In a pipeline
Run the dry run on every pull request and the apply on merge:
- run: |
curl -fsS -X PUT "$API/orgs/$ORG/manifest" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' --data @site.json
- if: github.ref == 'refs/heads/main'
run: |
curl -fsS -X PUT "$API/orgs/$ORG/manifest?apply=true" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' --data @site.jsonSend an Idempotency-Key on the apply if your runner retries: the first request runs and its response is remembered under that key for 24 hours, so a timeout at the edge can be retried without the work happening twice.
What it does not do
Content. A manifest describes the site — spaces, their settings and their structure. Pages, posts and releases are content, and they come in through Importing documentation, the CLI's
forumulate publish, or the API.Deletion. See
extrasabove. Removing a space is a deliberate act through the space's own endpoint.