Create the legal in an organization
POST
https://forumulate.com/api /orgs/{orgId}/legal Creates a legal document in a role and publishes its first version. Each edition is kept, so what somebody accepted stays readable after the text changes.
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
orgIdstringpathrequiredforumulate-versionstringheaderAPI 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-KeystringheaderMakes 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
kindstringrequiredtitlestringrequiredbodystringrequiredrequiredbooleanonThisPagestringWhether this document carries the "on this page" rail:
auto, always or never. Beats the legal space theme’s onThisPage; null clears it. A page-backed document sets onThisPage on its page’s metadata instead.customizationobjectThis document’s own
customCss, partial theme, shareImageUrl, ogTitle, ogDescription, twitterCard and jsonLd. Keys merge; null clears.Show child parameters
customCssstringCSS for THIS piece only, after the space theme so it wins. Sanitised and scoped to the reader exactly like the space
customCss (@import dropped, no way out of the <style>).themeobjectA partial theme for this piece — colours, palettes, fonts, typography, colorScheme, components, contentWidth, layout, motion, and (on a blog post)
post — layered over the space’s (which is over the site’s). On a post, release, event or legal document PATCH it MERGES key by key like the space theme’s PATCH, and null clears at any depth ({"paletteDark": null}); a page’s metadata replaces it whole. Unknown keys are refused by name. null clears all of it.Show child parameters
primaryColorstringaccentColorstringpaletteobjectpaletteDarkobjectfontstringcustomFontFamilystringheadingFontFamilystringbodyFontFamilystringmonoFontFamilystringcolorSchemestringcontentWidthstringlayoutobjectcomponentsobjectmotionobjectchangelogobjecttypographyobjectpostobjectshareImageUrlstringThe og:image / twitter:image for this piece, separate from its cover. Wins over the cover and over the generated social card.
ogTitlestringog:title / twitter:title.
ogDescriptionstringog:description / twitter:description.
twitterCardstringsummary (thumbnail) or summary_large_image (the default).jsonLdstringStructured data for this piece, as JSON (object or array of objects). Replaces the automatic markup on this piece (Article/BlogPosting, Event, DiscussionForumPosting/QAPage, the roadmap item’s Article).
null restores the automatic one.{
"kind": "string",
"title": "string",
"body": "string",
"required": true,
"onThisPage": "auto",
"customization": {
"customCss": "string",
"theme": {
"primaryColor": null,
"accentColor": null,
"palette": null,
"paletteDark": null,
"font": null,
"customFontFamily": null,
"headingFontFamily": null,
"bodyFontFamily": null,
"monoFontFamily": null,
"colorScheme": null,
"contentWidth": null,
"layout": null,
"components": null,
"motion": null,
"changelog": null,
"typography": null,
"post": null
},
"shareImageUrl": null,
"ogTitle": "string",
"ogDescription": "string",
"twitterCard": "summary",
"jsonLd": "string"
}
}Code samples
cURL
curl -X POST "https://forumulate.com/api/orgs/{orgId}/legal" \
-H "forumulate-version: <value>" \
-H "Idempotency-Key: <value>" \
-H "Content-Type: application/json" \
-d '{"kind":"string","title":"string","body":"string","required":true,"onThisPage":"auto","customization":{"customCss":"string","theme":{"primaryColor":null,"accentColor":null,"palette":null,"paletteDark":null,"font":null,"customFontFamily":null,"headingFontFamily":null,"bodyFontFamily":null,"monoFontFamily":null,"colorScheme":null,"contentWidth":null,"layout":null,"components":null,"motion":null,"changelog":null,"typography":null,"post":null},"shareImageUrl":null,"ogTitle":"string","ogDescription":"string","twitterCard":"summary","jsonLd":"string"}}'TypeScript
const res = await fetch('https://forumulate.com/api/orgs/{orgId}/legal', {
method: 'POST',
headers: {
'forumulate-version': '<value>',
'Idempotency-Key': '<value>',
'Content-Type': 'application/json',
},
body: JSON.stringify({"kind":"string","title":"string","body":"string","required":true,"onThisPage":"auto","customization":{"customCss":"string","theme":{"primaryColor":null,"accentColor":null,"palette":null,"paletteDark":null,"font":null,"customFontFamily":null,"headingFontFamily":null,"bodyFontFamily":null,"monoFontFamily":null,"colorScheme":null,"contentWidth":null,"layout":null,"components":null,"motion":null,"changelog":null,"typography":null,"post":null},"shareImageUrl":null,"ogTitle":"string","ogDescription":"string","twitterCard":"summary","jsonLd":"string"}}),
});
const data = await res.json();Python
import requests
res = requests.request(
"POST",
"https://forumulate.com/api/orgs/{orgId}/legal",
headers={
"forumulate-version": "<value>",
"Idempotency-Key": "<value>",
"Content-Type": "application/json",
},
json={"kind": "string", "title": "string", "body": "string", "required": True, "onThisPage": "auto", "customization": {"customCss": "string", "theme": {"primaryColor": None, "accentColor": None, "palette": None, "paletteDark": None, "font": None, "customFontFamily": None, "headingFontFamily": None, "bodyFontFamily": None, "monoFontFamily": None, "colorScheme": None, "contentWidth": None, "layout": None, "components": None, "motion": None, "changelog": None, "typography": None, "post": None}, "shareImageUrl": None, "ogTitle": "string", "ogDescription": "string", "twitterCard": "summary", "jsonLd": "string"}},
)
data = res.json()Go
package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
req, _ := http.NewRequest("POST", "https://forumulate.com/api/orgs/{orgId}/legal", strings.NewReader(`{"kind":"string","title":"string","body":"string","required":true,"onThisPage":"auto","customization":{"customCss":"string","theme":{"primaryColor":null,"accentColor":null,"palette":null,"paletteDark":null,"font":null,"customFontFamily":null,"headingFontFamily":null,"bodyFontFamily":null,"monoFontFamily":null,"colorScheme":null,"contentWidth":null,"layout":null,"components":null,"motion":null,"changelog":null,"typography":null,"post":null},"shareImageUrl":null,"ogTitle":"string","ogDescription":"string","twitterCard":"summary","jsonLd":"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}/legal');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
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, '{"kind":"string","title":"string","body":"string","required":true,"onThisPage":"auto","customization":{"customCss":"string","theme":{"primaryColor":null,"accentColor":null,"palette":null,"paletteDark":null,"font":null,"customFontFamily":null,"headingFontFamily":null,"bodyFontFamily":null,"monoFontFamily":null,"colorScheme":null,"contentWidth":null,"layout":null,"components":null,"motion":null,"changelog":null,"typography":null,"post":null},"shareImageUrl":null,"ogTitle":"string","ogDescription":"string","twitterCard":"summary","jsonLd":"string"}}');
$response = curl_exec($ch);
curl_close($ch);
echo $response;Ruby
require 'net/http'
require 'uri'
uri = URI('https://forumulate.com/api/orgs/{orgId}/legal')
req = Net::HTTP::Post.new(uri)
req['forumulate-version'] = '<value>'
req['Idempotency-Key'] = '<value>'
req['Content-Type'] = 'application/json'
req.body = '{"kind":"string","title":"string","body":"string","required":true,"onThisPage":"auto","customization":{"customCss":"string","theme":{"primaryColor":null,"accentColor":null,"palette":null,"paletteDark":null,"font":null,"customFontFamily":null,"headingFontFamily":null,"bodyFontFamily":null,"monoFontFamily":null,"colorScheme":null,"contentWidth":null,"layout":null,"components":null,"motion":null,"changelog":null,"typography":null,"post":null},"shareImageUrl":null,"ogTitle":"string","ogDescription":"string","twitterCard":"summary","jsonLd":"string"}}'
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts res.bodyJava
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}/legal"))
.header("forumulate-version", "<value>")
.header("Idempotency-Key", "<value>")
.header("Content-Type", "application/json")
.method("POST", BodyPublishers.ofString("{\"kind\":\"string\",\"title\":\"string\",\"body\":\"string\",\"required\":true,\"onThisPage\":\"auto\",\"customization\":{\"customCss\":\"string\",\"theme\":{\"primaryColor\":null,\"accentColor\":null,\"palette\":null,\"paletteDark\":null,\"font\":null,\"customFontFamily\":null,\"headingFontFamily\":null,\"bodyFontFamily\":null,\"monoFontFamily\":null,\"colorScheme\":null,\"contentWidth\":null,\"layout\":null,\"components\":null,\"motion\":null,\"changelog\":null,\"typography\":null,\"post\":null},\"shareImageUrl\":null,\"ogTitle\":\"string\",\"ogDescription\":\"string\",\"twitterCard\":\"summary\",\"jsonLd\":\"string\"}}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());Responses
200 Success.
Example
{
"document": {
"body": "string",
"content": {},
"customization": {
"customCss": "string",
"jsonLd": "string",
"ogDescription": "string",
"ogTitle": "string",
"shareImageUrl": "string",
"theme": {
"accentColor": "string",
"bodyFontFamily": "string",
"changelog": {
"archive": null,
"breakingBadge": null,
"channelBadge": null,
"closes": null,
"columnWidth": null,
"dateFormat": null,
"datePlacement": null,
"dateTone": null,
"entry": null,
"entryGap": null,
"facts": null,
"filters": null,
"group": null,
"gutter": null,
"gutterWidth": null,
"header": null,
"indexWidth": null,
"languages": null,
"look": null,
"prose": null,
"rail": null,
"release": null,
"sections": null,
"showTags": null,
"stickyDate": null,
"tagInk": null,
"tagOverflow": null,
"tagStyle": null,
"typeBadge": null,
"upgradeNotesFirst": null,
"versionBadge": null,
"widget": null
},
"colorScheme": "dark",
"components": {
"apiRef": null,
"button": null,
"buttonDark": null,
"byline": null,
"caption": null,
"card": null,
"code": null,
"codeDark": null,
"expandable": null,
"focus": null,
"hint": null,
"image": null,
"input": null,
"inputDark": null,
"kbd": null,
"link": null,
"list": null,
"meta": null,
"popover": null,
"preview": null,
"quote": null,
"radius": null,
"scrollbar": null,
"select": null,
"selection": null,
"shadow": null,
"skeleton": null,
"spacing": null,
"stepper": null,
"syntax": null,
"syntaxDark": null,
"syntaxTheme": null,
"table": null,
"tabs": null
},
"contentWidth": "full",
"customFontFamily": "string",
"font": "mono",
"headingFontFamily": "string",
"layout": {
"background": null,
"canvas": null,
"floatRoom": null,
"frame": null,
"guides": null,
"gutter": null,
"heroDecor": null,
"indexHeader": null,
"measure": null,
"widths": null
},
"monoFontFamily": "string",
"motion": {
"ambient": null,
"easing": null,
"enabled": null,
"header": null,
"hover": null,
"keyframes": null,
"pageCrossfade": null,
"pageTransition": null,
"preset": null,
"respectReducedMotion": null,
"reveal": null,
"scroll": null,
"speed": null,
"targets": null
},
"palette": {
"background": null,
"danger": null,
"footer_background": null,
"footer_primary": null,
"header_background": null,
"header_primary": null,
"heading": null,
"highlight": null,
"info": null,
"ink": null,
"line": null,
"love": null,
"muted": null,
"paper": null,
"primary": null,
"quaternary": null,
"secondary": null,
"success": null,
"surface": null,
"tertiary": null,
"tint": null,
"warning": null
},
"paletteDark": {
"background": null,
"danger": null,
"footer_background": null,
"footer_primary": null,
"header_background": null,
"header_primary": null,
"heading": null,
"highlight": null,
"info": null,
"ink": null,
"line": null,
"love": null,
"muted": null,
"paper": null,
"primary": null,
"quaternary": null,
"secondary": null,
"success": null,
"surface": null,
"tertiary": null,
"tint": null,
"warning": null
},
"post": {},
"primaryColor": "string",
"typography": {
"baseSize": null,
"bodyWrap": null,
"features": null,
"fluid": null,
"headingTransform": null,
"headingWrap": null,
"levels": null,
"numerals": null,
"opticalSizing": null,
"rhythm": null,
"scale": null,
"ui": null,
"variation": null
}
},
"twitterCard": "summary"
},
"fields": {},
"group": {
"description": null,
"icon": null,
"id": "string",
"name": "string",
"slug": "string",
"sortKey": "string",
"translations": {}
},
"id": "string",
"kind": "string",
"onThisPage": "always",
"pageId": "string",
"required": true,
"seoDescription": "string",
"spaceId": "string",
"title": "string",
"upcoming": {
"changeNote": null,
"effectiveAt": "2024-01-01T00:00:00Z",
"version": 0
},
"updatedAt": "2024-01-01T00:00:00Z",
"version": 0
},
"warnings": [
"tags: \"Launch Day\" is stored as \"launch-day\""
]
}Schema
documentobjectrequiredShow child parameters
bodystringrequiredcontentrequiredcustomizationobjectrequiredShow child parameters
customCssstringjsonLdstringogDescriptionstringogTitlestringshareImageUrlstringthemeobjecttwitterCardstringfieldsobjectrequiredgroupLegalGrouprequiredShow child parameters
descriptionstringrequirediconstringrequiredidstringrequirednamestringrequiredslugstringrequiredsortKeystringrequiredtranslationsobjectidstringrequiredkindstringrequiredonThisPagestringrequiredpageIdstringrequiredrequiredbooleanrequiredseoDescriptionstringrequiredspaceIdstringrequiredtitlestringrequiredupcomingobjectrequiredShow child parameters
changeNotestringrequiredeffectiveAtstringrequiredversionnumberrequiredupdatedAtstringrequiredversionnumberrequiredwarningsstring[]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
errorobjectrequiredShow child parameters
codestringrequiredStable, machine-readable error code, e.g.
rbac.forbidden.messagestringrequiredHuman-readable explanation.
detailsobjectField-level validation problems, keyed by field path (422 only).
docsstringWhere 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.reasonstringWhy 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.conflictobjectOn a 409 from a content entry field write (
PATCH …/entries/{entryId}/fields) whose If-Match was stale.Show child parameters
etagstringrequiredThe entry's current fields etag.
opsarrayrequiredThe 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.requestIdstringThe 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
errorobjectrequiredShow child parameters
codestringrequiredStable, machine-readable error code, e.g.
rbac.forbidden.messagestringrequiredHuman-readable explanation.
detailsobjectField-level validation problems, keyed by field path (422 only).
docsstringWhere 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.reasonstringWhy 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.conflictobjectOn a 409 from a content entry field write (
PATCH …/entries/{entryId}/fields) whose If-Match was stale.Show child parameters
etagstringrequiredThe entry's current fields etag.
opsarrayrequiredThe 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.requestIdstringThe 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
errorobjectrequiredShow child parameters
codestringrequiredStable, machine-readable error code, e.g.
rbac.forbidden.messagestringrequiredHuman-readable explanation.
detailsobjectField-level validation problems, keyed by field path (422 only).
docsstringWhere 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.reasonstringWhy 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.conflictobjectOn a 409 from a content entry field write (
PATCH …/entries/{entryId}/fields) whose If-Match was stale.Show child parameters
etagstringrequiredThe entry's current fields etag.
opsarrayrequiredThe 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.requestIdstringThe 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
errorobjectrequiredShow child parameters
codestringrequiredStable, machine-readable error code, e.g.
rbac.forbidden.messagestringrequiredHuman-readable explanation.
detailsobjectField-level validation problems, keyed by field path (422 only).
docsstringWhere 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.reasonstringWhy 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.conflictobjectOn a 409 from a content entry field write (
PATCH …/entries/{entryId}/fields) whose If-Match was stale.Show child parameters
etagstringrequiredThe entry's current fields etag.
opsarrayrequiredThe 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.requestIdstringThe 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
errorobjectrequiredShow child parameters
codestringrequiredStable, machine-readable error code, e.g.
rbac.forbidden.messagestringrequiredHuman-readable explanation.
detailsobjectField-level validation problems, keyed by field path (422 only).
docsstringWhere 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.reasonstringWhy 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.conflictobjectOn a 409 from a content entry field write (
PATCH …/entries/{entryId}/fields) whose If-Match was stale.Show child parameters
etagstringrequiredThe entry's current fields etag.
opsarrayrequiredThe 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.requestIdstringThe 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
errorobjectrequiredShow child parameters
codestringrequiredStable, machine-readable error code, e.g.
rbac.forbidden.messagestringrequiredHuman-readable explanation.
detailsobjectField-level validation problems, keyed by field path (422 only).
docsstringWhere 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.reasonstringWhy 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.conflictobjectOn a 409 from a content entry field write (
PATCH …/entries/{entryId}/fields) whose If-Match was stale.Show child parameters
etagstringrequiredThe entry's current fields etag.
opsarrayrequiredThe 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.requestIdstringThe 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
errorobjectrequiredShow child parameters
codestringrequiredStable, machine-readable error code, e.g.
rbac.forbidden.messagestringrequiredHuman-readable explanation.
detailsobjectField-level validation problems, keyed by field path (422 only).
docsstringWhere 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.reasonstringWhy 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.conflictobjectOn a 409 from a content entry field write (
PATCH …/entries/{entryId}/fields) whose If-Match was stale.Show child parameters
etagstringrequiredThe entry's current fields etag.
opsarrayrequiredThe 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.requestIdstringThe 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