Update the incident
PATCH
https://forumulate.com/api /orgs/{orgId}/spaces/{spaceId}/status/incidents/{incidentId} 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
orgIdstringpathrequiredspaceIdstringpathrequiredincidentIdstringpathrequiredforumulate-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
titlestringstatestringAn incident walks investigating → identified → monitoring → resolved. Maintenance may say the same steps in its own words — scheduled, in_progress, verifying, completed — which are stored as those four (
maintenanceState reads them back).severitystringnotice is informational — it moves no component and paints no day unless you link components with an impact. maintenance is the one severity with a scheduled window.bodyobjectThe incident write-up as a block document — or as a Markdown string, converted like a page body. Or send
markdown.markdownstringThe body as Markdown (CommonMark + tables/task lists), converted server-side. Alternative to
content. Blocks beyond plain Markdown (callouts, tabs, cards, steppers, columns, buttons, …) are written as :::name{attrs} directives — every directive, its attributes and how it closes: GET /docs/markdown-dialect. Up to 2,000,000 characters of Markdown — the same ceiling the importer, the create and the update doors all use, so an imported page can always be written back.fieldsobjectShow child parameters
componentsstringpostmortemUrlstringcomponentsobject[]The components this incident affects, each with its impact. While the incident is open each one reads at least that state, and the days it spans take that colour on the component’s bars. Sent, it replaces the set;
[] clears it.Show child parameters
monitorIdstringrequiredimpactstringscheduledForstringscheduledUntilstringstartedAtstringWhen the incident actually began, if not when it was posted. Clamped to now; a backfill is about the past.
resolvedAtstringWhen it ended — only on an incident that is (or is being made)
resolved. Posting one already resolved marks it Retroactive on the public page.autoStartbooleanMaintenance: move to in progress at
scheduledFor (default true). Subscribers are told.autoCompletebooleanMaintenance: complete at
scheduledUntil (default true). Off, it stays in progress until you complete it — for work you verify by hand.audienceobjectWho may see this on the public page:
everyone (default), signedIn (any signed-in reader) or groups (a signed-in member of one of groups). Hidden rows are dropped by the API before anything is sent. The machine doors — summary.json, api/v2, the feeds, the badge and the widget — always answer as an anonymous reader.Show child parameters
whostringrequiredgroupsstring[]notifySubscribersbooleanEmail the page's confirmed subscribers and push to its Slack/Discord/webhook channels. Default true. Delivery is queued, so the response does not wait for it. On PATCH, only a state change is announced.
{
"title": "string",
"state": "investigating",
"severity": "notice",
"body": {},
"markdown": "string",
"fields": {
"components": "string",
"postmortemUrl": "string"
},
"components": [
{
"monitorId": "string",
"impact": "partial_outage"
}
],
"scheduledFor": "2024-01-01T00:00:00Z",
"scheduledUntil": "2024-01-01T00:00:00Z",
"startedAt": "2024-01-01T00:00:00Z",
"resolvedAt": "2024-01-01T00:00:00Z",
"autoStart": true,
"autoComplete": true,
"audience": {
"who": "everyone",
"groups": []
},
"notifySubscribers": true
}Code samples
cURL
curl -X PATCH "https://forumulate.com/api/orgs/{orgId}/spaces/{spaceId}/status/incidents/{incidentId}" \
-H "forumulate-version: <value>" \
-H "Idempotency-Key: <value>" \
-H "Content-Type: application/json" \
-d '{"title":"string","state":"investigating","severity":"notice","body":{},"markdown":"string","fields":{"components":"string","postmortemUrl":"string"},"components":[{"monitorId":"string","impact":"partial_outage"}],"scheduledFor":"2024-01-01T00:00:00Z","scheduledUntil":"2024-01-01T00:00:00Z","startedAt":"2024-01-01T00:00:00Z","resolvedAt":"2024-01-01T00:00:00Z","autoStart":true,"autoComplete":true,"audience":{"who":"everyone","groups":[]},"notifySubscribers":true}'TypeScript
const res = await fetch('https://forumulate.com/api/orgs/{orgId}/spaces/{spaceId}/status/incidents/{incidentId}', {
method: 'PATCH',
headers: {
'forumulate-version': '<value>',
'Idempotency-Key': '<value>',
'Content-Type': 'application/json',
},
body: JSON.stringify({"title":"string","state":"investigating","severity":"notice","body":{},"markdown":"string","fields":{"components":"string","postmortemUrl":"string"},"components":[{"monitorId":"string","impact":"partial_outage"}],"scheduledFor":"2024-01-01T00:00:00Z","scheduledUntil":"2024-01-01T00:00:00Z","startedAt":"2024-01-01T00:00:00Z","resolvedAt":"2024-01-01T00:00:00Z","autoStart":true,"autoComplete":true,"audience":{"who":"everyone","groups":[]},"notifySubscribers":true}),
});
const data = await res.json();Python
import requests
res = requests.request(
"PATCH",
"https://forumulate.com/api/orgs/{orgId}/spaces/{spaceId}/status/incidents/{incidentId}",
headers={
"forumulate-version": "<value>",
"Idempotency-Key": "<value>",
"Content-Type": "application/json",
},
json={"title": "string", "state": "investigating", "severity": "notice", "body": {}, "markdown": "string", "fields": {"components": "string", "postmortemUrl": "string"}, "components": [{"monitorId": "string", "impact": "partial_outage"}], "scheduledFor": "2024-01-01T00:00:00Z", "scheduledUntil": "2024-01-01T00:00:00Z", "startedAt": "2024-01-01T00:00:00Z", "resolvedAt": "2024-01-01T00:00:00Z", "autoStart": True, "autoComplete": True, "audience": {"who": "everyone", "groups": []}, "notifySubscribers": True},
)
data = res.json()Go
package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
req, _ := http.NewRequest("PATCH", "https://forumulate.com/api/orgs/{orgId}/spaces/{spaceId}/status/incidents/{incidentId}", strings.NewReader(`{"title":"string","state":"investigating","severity":"notice","body":{},"markdown":"string","fields":{"components":"string","postmortemUrl":"string"},"components":[{"monitorId":"string","impact":"partial_outage"}],"scheduledFor":"2024-01-01T00:00:00Z","scheduledUntil":"2024-01-01T00:00:00Z","startedAt":"2024-01-01T00:00:00Z","resolvedAt":"2024-01-01T00:00:00Z","autoStart":true,"autoComplete":true,"audience":{"who":"everyone","groups":[]},"notifySubscribers":true}`))
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}/spaces/{spaceId}/status/incidents/{incidentId}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
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, '{"title":"string","state":"investigating","severity":"notice","body":{},"markdown":"string","fields":{"components":"string","postmortemUrl":"string"},"components":[{"monitorId":"string","impact":"partial_outage"}],"scheduledFor":"2024-01-01T00:00:00Z","scheduledUntil":"2024-01-01T00:00:00Z","startedAt":"2024-01-01T00:00:00Z","resolvedAt":"2024-01-01T00:00:00Z","autoStart":true,"autoComplete":true,"audience":{"who":"everyone","groups":[]},"notifySubscribers":true}');
$response = curl_exec($ch);
curl_close($ch);
echo $response;Ruby
require 'net/http'
require 'uri'
uri = URI('https://forumulate.com/api/orgs/{orgId}/spaces/{spaceId}/status/incidents/{incidentId}')
req = Net::HTTP::Patch.new(uri)
req['forumulate-version'] = '<value>'
req['Idempotency-Key'] = '<value>'
req['Content-Type'] = 'application/json'
req.body = '{"title":"string","state":"investigating","severity":"notice","body":{},"markdown":"string","fields":{"components":"string","postmortemUrl":"string"},"components":[{"monitorId":"string","impact":"partial_outage"}],"scheduledFor":"2024-01-01T00:00:00Z","scheduledUntil":"2024-01-01T00:00:00Z","startedAt":"2024-01-01T00:00:00Z","resolvedAt":"2024-01-01T00:00:00Z","autoStart":true,"autoComplete":true,"audience":{"who":"everyone","groups":[]},"notifySubscribers":true}'
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}/spaces/{spaceId}/status/incidents/{incidentId}"))
.header("forumulate-version", "<value>")
.header("Idempotency-Key", "<value>")
.header("Content-Type", "application/json")
.method("PATCH", BodyPublishers.ofString("{\"title\":\"string\",\"state\":\"investigating\",\"severity\":\"notice\",\"body\":{},\"markdown\":\"string\",\"fields\":{\"components\":\"string\",\"postmortemUrl\":\"string\"},\"components\":[{\"monitorId\":\"string\",\"impact\":\"partial_outage\"}],\"scheduledFor\":\"2024-01-01T00:00:00Z\",\"scheduledUntil\":\"2024-01-01T00:00:00Z\",\"startedAt\":\"2024-01-01T00:00:00Z\",\"resolvedAt\":\"2024-01-01T00:00:00Z\",\"autoStart\":true,\"autoComplete\":true,\"audience\":{\"who\":\"everyone\",\"groups\":[]},\"notifySubscribers\":true}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());Responses
200 Success.
Example
{
"incident": {
"audience": {
"groups": [
"string"
],
"who": "everyone"
},
"autoComplete": true,
"autoStart": true,
"body": {},
"components": [
{
"impact": "degraded",
"monitorId": "string",
"name": "string",
"slug": null
}
],
"fields": {},
"id": "string",
"locale": "string",
"machineTranslated": true,
"maintenanceState": "completed",
"resolvedAt": "string",
"retroactive": true,
"scheduledFor": "string",
"scheduledUntil": "string",
"severity": "string",
"startedAt": "string",
"state": "string",
"title": "string",
"updatedAt": "string",
"updates": [
{
"body": "string",
"createdAt": "2024-01-01T00:00:00Z",
"id": "string",
"state": null
}
]
},
"warnings": [
"tags: \"Launch Day\" is stored as \"launch-day\""
]
}Schema
incidentobjectrequiredShow child parameters
audienceobjectShow child parameters
groupsstring[]requiredwhostringrequiredautoCompletebooleanrequiredautoStartbooleanrequiredbodyobjectrequiredcomponentsAffectedComponent[]requiredShow child parameters
impactstringrequiredmonitorIdstringrequirednamestringrequiredslugstringrequiredfieldsobjectrequiredidstringrequiredlocalestringmachineTranslatedbooleanmaintenanceStatestringrequiredresolvedAtstringrequiredretroactivebooleanrequiredscheduledForstringrequiredscheduledUntilstringrequiredseveritystringrequiredstartedAtstringrequiredstatestringrequiredtitlestringrequiredupdatedAtstringrequiredupdatesIncidentUpdate[]requiredShow child parameters
bodystringrequiredcreatedAtstringrequiredidstringstatestringrequiredwarningsstring[]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