Replace the library's content model

Embed
Preview of the card
<a href="https://forumulate.com/reference/forumulate-api-contentmodel-put"><img src="https://forumulate.com/reference/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/8042f004-e3d9-445f-b7a4-b6c1f86836e6/pages/5972a45b-5249-4215-b64f-75545a091e29/social-card.svg?v=3e7ae33eec8b" alt="Replace the library's content model" width="600" height="315"></a>
PUThttps://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model

Saves the library-wide shape and the content types, and records a new model version with the declared ops. A renameField/renameType/renameBlockType moves stored values to the new name; a removeField hides them for 30 days (undo with restoreField). Values are rewritten behind the response: GET …/model/versions shows migratedAt once done.

Requires token scope: write (the coarse method gate) for Atlas key (ak_), Personal Access Token (cft_) or OAuth (cfo_) auth — in addition to any catalog scope (GET /pats/scopes) the route’s capability needs. Session/cookie auth is governed by RBAC capabilities instead.

Parameters
orgIdstringpathrequired
libraryIdstringpathrequired
forumulate-versionstringheader
API version negotiation. Send forumulate-version: 2026-12-01 on every request from a new integration; it is optional only so that integrations written before versioning keep working. Each version includes everything before it:
Idempotency-Keystringheader
Makes this write safe to retry. Optional — send any unique string (a UUID is ideal) and the first request runs while its response is remembered under that key for 24 hours; an identical retry replays that same status and body instead of acting twice, so a timeout or a 5xx from the edge can be retried without creating a second row. A retry sent while the first request is still in flight is refused with 409. The key is scoped to the method and path, so the same key on a different endpoint is a different request. Error responses of 5xx are never cached — those retries re-run. A key is bound to the BODY it was first used with: reusing it with a different body is a 409 idempotency.key_reused, not a replay, because replaying would tell you a write landed that never ran. Generate one key per request, never per process or per session.
Request body application/json
shapeobject
The library-wide shape every content type inherits. Omitted: unchanged.
Show child parameters
spaceobject
Show child parameters
indexstring
landing | list | board | single
orderstring
manual | chronological | activity | votes | status
navstring
tree | categories | tags | sections | none
whoCreatesstring
staff | members | anyone
summarystring[]
sectionsobject
Show child parameters
addobject[]
omitstring[]
orderstring[]
setobject
unsetobject
paramsobject
Show child parameters
addobject[]
omitstring[]
orderstring[]
setobject
unsetobject
blockTypesobject
Show child parameters
addobject[]
omitstring[]
orderstring[]
setobject
unsetobject
typesobject
Every content type, by key. REPLACES the set; omitted: unchanged.
opsobject[]
What changed, where a diff cannot tell: a rename is not a removal plus an addition. Checked against both models.
{
  "shape": {
    "space": {
      "index": "landing",
      "order": "manual",
      "nav": "tree",
      "whoCreates": "staff",
      "summary": [
        "string"
      ]
    },
    "sections": {
      "add": [
        {
          "key": null,
          "label": null,
          "kind": null,
          "required": null,
          "hint": null,
          "example": null,
          "schemaProperty": null,
          "render": null
        }
      ],
      "omit": [
        "string"
      ],
      "order": [
        "string"
      ],
      "set": {},
      "unset": {}
    },
    "params": {
      "add": [
        {
          "key": null,
          "label": null,
          "type": null,
          "options": null,
          "relationSpaceId": null,
          "hint": null,
          "placeholder": null,
          "internal": null,
          "facet": null,
          "meta": null,
          "required": null,
          "default": null,
          "schemaProperty": null,
          "min": null,
          "max": null,
          "visibility": null,
          "localized": null,
          "unique": null,
          "indexed": null,
          "sortable": null,
          "minLength": null,
          "maxLength": null,
          "pattern": null,
          "condition": null,
          "expr": null,
          "schema": null,
          "allowedBlocks": null,
          "allowed": null,
          "allowedGroups": null,
          "to": null,
          "hasMany": null,
          "rowLabelField": null,
          "language": null,
          "from": null,
          "accept": null,
          "admin": null,
          "fields": null
        }
      ],
      "omit": [
        "string"
      ],
      "order": [
        "string"
      ],
      "set": {},
      "unset": {}
    },
    "blockTypes": {
      "add": [
        {
          "key": null,
          "label": null,
          "description": null,
          "icon": null,
          "group": null,
          "fields": null,
          "variants": null
        }
      ],
      "omit": [
        "string"
      ],
      "order": [
        "string"
      ],
      "set": {},
      "unset": {}
    }
  },
  "types": {},
  "ops": [
    {
      "op": "string",
      "type": "string",
      "blockType": "string",
      "variant": "string",
      "from": "string",
      "to": "string"
    }
  ]
}
Code samples
cURL
curl -X PUT "https://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model" \
  -H "forumulate-version: <value>" \
  -H "Idempotency-Key: <value>" \
  -H "Content-Type: application/json" \
  -d '{"shape":{"space":{"index":"landing","order":"manual","nav":"tree","whoCreates":"staff","summary":["string"]},"sections":{"add":[{"key":null,"label":null,"kind":null,"required":null,"hint":null,"example":null,"schemaProperty":null,"render":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"params":{"add":[{"key":null,"label":null,"type":null,"options":null,"relationSpaceId":null,"hint":null,"placeholder":null,"internal":null,"facet":null,"meta":null,"required":null,"default":null,"schemaProperty":null,"min":null,"max":null,"visibility":null,"localized":null,"unique":null,"indexed":null,"sortable":null,"minLength":null,"maxLength":null,"pattern":null,"condition":null,"expr":null,"schema":null,"allowedBlocks":null,"allowed":null,"allowedGroups":null,"to":null,"hasMany":null,"rowLabelField":null,"language":null,"from":null,"accept":null,"admin":null,"fields":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"blockTypes":{"add":[{"key":null,"label":null,"description":null,"icon":null,"group":null,"fields":null,"variants":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}}},"types":{},"ops":[{"op":"string","type":"string","blockType":"string","variant":"string","from":"string","to":"string"}]}'
TypeScript
const res = await fetch('https://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model', {
  method: 'PUT',
  headers: {
    'forumulate-version': '<value>',
    'Idempotency-Key': '<value>',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({"shape":{"space":{"index":"landing","order":"manual","nav":"tree","whoCreates":"staff","summary":["string"]},"sections":{"add":[{"key":null,"label":null,"kind":null,"required":null,"hint":null,"example":null,"schemaProperty":null,"render":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"params":{"add":[{"key":null,"label":null,"type":null,"options":null,"relationSpaceId":null,"hint":null,"placeholder":null,"internal":null,"facet":null,"meta":null,"required":null,"default":null,"schemaProperty":null,"min":null,"max":null,"visibility":null,"localized":null,"unique":null,"indexed":null,"sortable":null,"minLength":null,"maxLength":null,"pattern":null,"condition":null,"expr":null,"schema":null,"allowedBlocks":null,"allowed":null,"allowedGroups":null,"to":null,"hasMany":null,"rowLabelField":null,"language":null,"from":null,"accept":null,"admin":null,"fields":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"blockTypes":{"add":[{"key":null,"label":null,"description":null,"icon":null,"group":null,"fields":null,"variants":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}}},"types":{},"ops":[{"op":"string","type":"string","blockType":"string","variant":"string","from":"string","to":"string"}]}),
});
const data = await res.json();
Python
import requests

res = requests.request(
    "PUT",
    "https://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model",
    headers={
    "forumulate-version": "<value>",
    "Idempotency-Key": "<value>",
    "Content-Type": "application/json",
    },
    json={"shape": {"space": {"index": "landing", "order": "manual", "nav": "tree", "whoCreates": "staff", "summary": ["string"]}, "sections": {"add": [{"key": None, "label": None, "kind": None, "required": None, "hint": None, "example": None, "schemaProperty": None, "render": None}], "omit": ["string"], "order": ["string"], "set": {}, "unset": {}}, "params": {"add": [{"key": None, "label": None, "type": None, "options": None, "relationSpaceId": None, "hint": None, "placeholder": None, "internal": None, "facet": None, "meta": None, "required": None, "default": None, "schemaProperty": None, "min": None, "max": None, "visibility": None, "localized": None, "unique": None, "indexed": None, "sortable": None, "minLength": None, "maxLength": None, "pattern": None, "condition": None, "expr": None, "schema": None, "allowedBlocks": None, "allowed": None, "allowedGroups": None, "to": None, "hasMany": None, "rowLabelField": None, "language": None, "from": None, "accept": None, "admin": None, "fields": None}], "omit": ["string"], "order": ["string"], "set": {}, "unset": {}}, "blockTypes": {"add": [{"key": None, "label": None, "description": None, "icon": None, "group": None, "fields": None, "variants": None}], "omit": ["string"], "order": ["string"], "set": {}, "unset": {}}}, "types": {}, "ops": [{"op": "string", "type": "string", "blockType": "string", "variant": "string", "from": "string", "to": "string"}]},
)
data = res.json()
Go
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	req, _ := http.NewRequest("PUT", "https://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model", strings.NewReader(`{"shape":{"space":{"index":"landing","order":"manual","nav":"tree","whoCreates":"staff","summary":["string"]},"sections":{"add":[{"key":null,"label":null,"kind":null,"required":null,"hint":null,"example":null,"schemaProperty":null,"render":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"params":{"add":[{"key":null,"label":null,"type":null,"options":null,"relationSpaceId":null,"hint":null,"placeholder":null,"internal":null,"facet":null,"meta":null,"required":null,"default":null,"schemaProperty":null,"min":null,"max":null,"visibility":null,"localized":null,"unique":null,"indexed":null,"sortable":null,"minLength":null,"maxLength":null,"pattern":null,"condition":null,"expr":null,"schema":null,"allowedBlocks":null,"allowed":null,"allowedGroups":null,"to":null,"hasMany":null,"rowLabelField":null,"language":null,"from":null,"accept":null,"admin":null,"fields":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"blockTypes":{"add":[{"key":null,"label":null,"description":null,"icon":null,"group":null,"fields":null,"variants":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}}},"types":{},"ops":[{"op":"string","type":"string","blockType":"string","variant":"string","from":"string","to":"string"}]}`))
	req.Header.Set("forumulate-version", "<value>")
	req.Header.Set("Idempotency-Key", "<value>")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}
PHP
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['forumulate-version: <value>', 'Idempotency-Key: <value>', 'Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"shape":{"space":{"index":"landing","order":"manual","nav":"tree","whoCreates":"staff","summary":["string"]},"sections":{"add":[{"key":null,"label":null,"kind":null,"required":null,"hint":null,"example":null,"schemaProperty":null,"render":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"params":{"add":[{"key":null,"label":null,"type":null,"options":null,"relationSpaceId":null,"hint":null,"placeholder":null,"internal":null,"facet":null,"meta":null,"required":null,"default":null,"schemaProperty":null,"min":null,"max":null,"visibility":null,"localized":null,"unique":null,"indexed":null,"sortable":null,"minLength":null,"maxLength":null,"pattern":null,"condition":null,"expr":null,"schema":null,"allowedBlocks":null,"allowed":null,"allowedGroups":null,"to":null,"hasMany":null,"rowLabelField":null,"language":null,"from":null,"accept":null,"admin":null,"fields":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"blockTypes":{"add":[{"key":null,"label":null,"description":null,"icon":null,"group":null,"fields":null,"variants":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}}},"types":{},"ops":[{"op":"string","type":"string","blockType":"string","variant":"string","from":"string","to":"string"}]}');
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Ruby
require 'net/http'
require 'uri'

uri = URI('https://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model')
req = Net::HTTP::Put.new(uri)
req['forumulate-version'] = '<value>'
req['Idempotency-Key'] = '<value>'
req['Content-Type'] = 'application/json'
req.body = '{"shape":{"space":{"index":"landing","order":"manual","nav":"tree","whoCreates":"staff","summary":["string"]},"sections":{"add":[{"key":null,"label":null,"kind":null,"required":null,"hint":null,"example":null,"schemaProperty":null,"render":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"params":{"add":[{"key":null,"label":null,"type":null,"options":null,"relationSpaceId":null,"hint":null,"placeholder":null,"internal":null,"facet":null,"meta":null,"required":null,"default":null,"schemaProperty":null,"min":null,"max":null,"visibility":null,"localized":null,"unique":null,"indexed":null,"sortable":null,"minLength":null,"maxLength":null,"pattern":null,"condition":null,"expr":null,"schema":null,"allowedBlocks":null,"allowed":null,"allowedGroups":null,"to":null,"hasMany":null,"rowLabelField":null,"language":null,"from":null,"accept":null,"admin":null,"fields":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}},"blockTypes":{"add":[{"key":null,"label":null,"description":null,"icon":null,"group":null,"fields":null,"variants":null}],"omit":["string"],"order":["string"],"set":{},"unset":{}}},"types":{},"ops":[{"op":"string","type":"string","blockType":"string","variant":"string","from":"string","to":"string"}]}'
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  http.request(req)
end
puts res.body
Java
import java.net.URI;
import java.net.http.*;
import java.net.http.HttpRequest.BodyPublishers;

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://forumulate.com/api/orgs/{orgId}/content/libraries/{libraryId}/model"))
    .header("forumulate-version", "<value>")
    .header("Idempotency-Key", "<value>")
    .header("Content-Type", "application/json")
    .method("PUT", BodyPublishers.ofString("{\"shape\":{\"space\":{\"index\":\"landing\",\"order\":\"manual\",\"nav\":\"tree\",\"whoCreates\":\"staff\",\"summary\":[\"string\"]},\"sections\":{\"add\":[{\"key\":null,\"label\":null,\"kind\":null,\"required\":null,\"hint\":null,\"example\":null,\"schemaProperty\":null,\"render\":null}],\"omit\":[\"string\"],\"order\":[\"string\"],\"set\":{},\"unset\":{}},\"params\":{\"add\":[{\"key\":null,\"label\":null,\"type\":null,\"options\":null,\"relationSpaceId\":null,\"hint\":null,\"placeholder\":null,\"internal\":null,\"facet\":null,\"meta\":null,\"required\":null,\"default\":null,\"schemaProperty\":null,\"min\":null,\"max\":null,\"visibility\":null,\"localized\":null,\"unique\":null,\"indexed\":null,\"sortable\":null,\"minLength\":null,\"maxLength\":null,\"pattern\":null,\"condition\":null,\"expr\":null,\"schema\":null,\"allowedBlocks\":null,\"allowed\":null,\"allowedGroups\":null,\"to\":null,\"hasMany\":null,\"rowLabelField\":null,\"language\":null,\"from\":null,\"accept\":null,\"admin\":null,\"fields\":null}],\"omit\":[\"string\"],\"order\":[\"string\"],\"set\":{},\"unset\":{}},\"blockTypes\":{\"add\":[{\"key\":null,\"label\":null,\"description\":null,\"icon\":null,\"group\":null,\"fields\":null,\"variants\":null}],\"omit\":[\"string\"],\"order\":[\"string\"],\"set\":{},\"unset\":{}}},\"types\":{},\"ops\":[{\"op\":\"string\",\"type\":\"string\",\"blockType\":\"string\",\"variant\":\"string\",\"from\":\"string\",\"to\":\"string\"}]}"))
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Responses
200 Success.
Example
{
  "model": {
    "shape": {},
    "types": {},
    "version": 0
  },
  "warnings": [
    "tags: \"Launch Day\" is stored as \"launch-day\""
  ]
}
Schema
modelobjectrequired
Show child parameters
shapeobjectrequired
typesobjectrequired
versionnumberrequired
warningsstring[]
Values we ACCEPTED and then rewrote, one sentence each — a slugified tag, a normalised colour, a clamped range, a slug that was taken. Absent when nothing was rewritten, never []. The same sentences are on the X-Field-Warnings response header. If this is present, something you sent is not what is stored; read the resource back rather than assuming your own value.
400 The request could not be understood — a malformed header, path or query value (request.malformed). A value that was understood but is not acceptable is a 422 validation.failed.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
401 No credential, or one that is expired or revoked (auth.required). Send a Bearer token or the session cookie.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
403 Authenticated, and not allowed — always rbac.forbidden, whether what is missing is the capability, the token scope, or a resource the token is pinned away from. The message names the capability and, for a scope refusal, the scopes that would grant it. A refusal because of the PLAN is a 402 with quota.exceeded, not this.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
404 No such thing — one of the ids in the path does not resolve, or resolves to something this caller may not see (*.not_found).
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
422 The body was understood and a value is unacceptable (validation.failed). error.details names the field paths.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
429 Rate limited — see the RateLimit-* headers.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
default Error — the stable { error: { code, message } } envelope.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
The Forumulate teamUpdated