Replace the organization’s policy gate defaults

Embed
Preview of the card
<a href="https://forumulate.com/reference/forumulate-api-policygateadmin-putorg"><img src="https://forumulate.com/reference/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/8042f004-e3d9-445f-b7a4-b6c1f86836e6/pages/e9f6fea4-0fe7-4123-8a30-9704aaafe5c6/social-card.svg?v=3e7ae33eec8b" alt="Replace the organization’s policy gate defaults" width="600" height="315"></a>
PUThttps://forumulate.com/api/orgs/{orgId}/policy-gate/config

Replaces the stored organization policy gate defaults with the body: a key you leave out is removed (use PATCH to change part of it). ?dryRun=true answers the result and its diff without storing; ?prune=true adds the diff to a real write.

Replaces the org level (not a merge: leaving a key out is how a level goes back to inheriting it). Strict — an unknown key is a 422 that names its path. A GET sent back is taken as its stored. See the policy acceptance gate guide for every key.

Requires token scope: write — see "Token scopes" under Authentication.

Parameters

orgIdstringpathrequired
The organization: its uuid, or its slug. One you are not a member of is a 404.
dryRunstringquery
true (or 1): read, merge and validate exactly as the write would, store NOTHING, and answer the would-be result with dryRun: true and diff — every key it would add, remove or change ({path, op, from?, to?}, sorted by path; secrets appear only as <key>Set). Anything other than true/false is a 422.
true | 1 | yes | on | false | 0 | no | off
prunestringquery
true (or 1): the stored object becomes EXACTLY this body — every key you leave out is removed — and the answer carries diff naming what changed, removals included. Combine with dryRun=true to read that diff first.
true | 1 | yes | on | false | 0 | no | off
forumulate-versionstringheader
API version negotiation. Send forumulate-version: 2027-03-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
enabledboolean
defaultsobject
Show child parameters
presentationstring
checkbox | checkbox-modal | modal | inline-scroll | page | link
requiredboolean
eventsstring[]
reacceptstring
never | next_sign_in | grace_days
graceDaysinteger
requireScrollboolean
showSummaryboolean
declinestring
allow | block
dismissibleboolean
policiesobject
uiobject
Show child parameters
groupingstring
combined | sequence
readerstring
modal | new_tab
layoutstring
center | sheet | fullscreen
mobileFullscreenboolean
inheritThemeboolean
varsobject
Show child parameters
accentstring
accent-contraststring
backgroundstring
surfacestring
textstring
mutedstring
borderstring
radiusstring
fontstring
shadowstring
backdropstring
focusstring
max-widthstring
localestring
labelsobject
embedobject
Show child parameters
allowedOriginsstring[]
requireTicketboolean
tokenTtlSecondsinteger
pageobject
Show child parameters
returnUrlsstring[]
readerobject
Show child parameters
promptstring
off | widget
{
  "enabled": true,
  "defaults": {
    "presentation": "checkbox",
    "required": true,
    "events": [
      "sign_up"
    ],
    "reaccept": "never",
    "graceDays": 0,
    "requireScroll": true,
    "showSummary": true,
    "decline": "allow",
    "dismissible": true
  },
  "policies": {},
  "ui": {
    "grouping": "combined",
    "reader": "modal",
    "layout": "center",
    "mobileFullscreen": true,
    "inheritTheme": true,
    "vars": {
      "accent": "string",
      "accent-contrast": "string",
      "background": "string",
      "surface": "string",
      "text": "string",
      "muted": "string",
      "border": "string",
      "radius": "string",
      "font": "string",
      "shadow": "string",
      "backdrop": "string",
      "focus": "string",
      "max-width": "string"
    },
    "locale": "string",
    "labels": {}
  },
  "embed": {
    "allowedOrigins": [
      "string"
    ],
    "requireTicket": true,
    "tokenTtlSeconds": 0
  },
  "page": {
    "returnUrls": [
      "string"
    ]
  },
  "reader": {
    "prompt": "off"
  }
}

Code samples

cURL
curl -X PUT "https://forumulate.com/api/orgs/{orgId}/policy-gate/config" \
  -H "Authorization: Bearer $FORUMULATE_API_KEY" \
  -H "forumulate-version: <value>" \
  -H "Idempotency-Key: <value>" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"defaults":{"presentation":"checkbox","required":true,"events":["sign_up"],"reaccept":"never","graceDays":0,"requireScroll":true,"showSummary":true,"decline":"allow","dismissible":true},"policies":{},"ui":{"grouping":"combined","reader":"modal","layout":"center","mobileFullscreen":true,"inheritTheme":true,"vars":{"accent":"string","accent-contrast":"string","background":"string","surface":"string","text":"string","muted":"string","border":"string","radius":"string","font":"string","shadow":"string","backdrop":"string","focus":"string","max-width":"string"},"locale":"string","labels":{}},"embed":{"allowedOrigins":["string"],"requireTicket":true,"tokenTtlSeconds":0},"page":{"returnUrls":["string"]},"reader":{"prompt":"off"}}'
TypeScript
Python
Go
PHP
Ruby
Java

Responses

200 Success.
Example
{
  "diff": [
    {
      "from": {},
      "op": "add",
      "path": "string",
      "to": {}
    }
  ],
  "dryRun": true,
  "org": {
    "defaults": {
      "decline": "allow",
      "dismissible": true,
      "events": [
        "sign_in"
      ],
      "graceDays": 0,
      "presentation": "checkbox",
      "reaccept": "grace_days",
      "required": true,
      "requireScroll": true,
      "showSummary": true
    },
    "embed": {
      "allowedOrigins": [
        "string"
      ],
      "requireTicket": true,
      "tokenTtlSeconds": 0
    },
    "enabled": true,
    "page": {
      "returnUrls": [
        "string"
      ]
    },
    "policies": {},
    "reader": {
      "prompt": "off"
    },
    "ui": {
      "grouping": "combined",
      "inheritTheme": true,
      "labels": {},
      "layout": "center",
      "locale": "string",
      "mobileFullscreen": true,
      "reader": "modal",
      "vars": {
        "accent": "string",
        "accent-contrast": "string",
        "backdrop": "string",
        "background": "string",
        "border": "string",
        "focus": "string",
        "font": "string",
        "max-width": "string",
        "muted": "string",
        "radius": "string",
        "shadow": "string",
        "surface": "string",
        "text": "string"
      }
    }
  },
  "resolved": {
    "embed": {
      "allowedOrigins": [
        "string"
      ],
      "requireTicket": true,
      "tokenTtlSeconds": 0
    },
    "enabled": true,
    "enabledSource": "call",
    "page": {
      "returnUrls": [
        "string"
      ]
    },
    "reader": {
      "prompt": "off"
    },
    "ui": {
      "grouping": "combined",
      "inheritTheme": true,
      "labels": {},
      "layout": "center",
      "locale": "string",
      "mobileFullscreen": true,
      "reader": "modal",
      "vars": {
        "accent": {},
        "accent-contrast": {},
        "backdrop": {},
        "background": {},
        "border": {},
        "focus": {},
        "font": {},
        "max-width": {},
        "muted": {},
        "radius": {},
        "shadow": {},
        "surface": {},
        "text": {}
      }
    }
  },
  "stored": {
    "defaults": {
      "decline": "allow",
      "dismissible": true,
      "events": [
        "sign_in"
      ],
      "graceDays": 0,
      "presentation": "checkbox",
      "reaccept": "grace_days",
      "required": true,
      "requireScroll": true,
      "showSummary": true
    },
    "embed": {
      "allowedOrigins": [
        "string"
      ],
      "requireTicket": true,
      "tokenTtlSeconds": 0
    },
    "enabled": true,
    "page": {
      "returnUrls": [
        "string"
      ]
    },
    "policies": {},
    "reader": {
      "prompt": "off"
    },
    "ui": {
      "grouping": "combined",
      "inheritTheme": true,
      "labels": {},
      "layout": "center",
      "locale": "string",
      "mobileFullscreen": true,
      "reader": "modal",
      "vars": {
        "accent": "string",
        "accent-contrast": "string",
        "backdrop": "string",
        "background": "string",
        "border": "string",
        "focus": "string",
        "font": "string",
        "max-width": "string",
        "muted": "string",
        "radius": "string",
        "shadow": "string",
        "surface": "string",
        "text": "string"
      }
    }
  },
  "warnings": [
    "tags: \"Launch Day\" is stored as \"launch-day\""
  ]
}
Schema
diffSiteConfigChange[]
Show child parameters
fromobject
opstringrequired
add | change | remove
pathstringrequired
toobject
dryRunboolean
orgobjectrequired
Show child parameters
defaultsobject
Show child parameters
declinestring
allow | block
dismissibleboolean
eventsstring[]
graceDaysnumber
presentationstring
checkbox | checkbox-modal | inline-scroll | link | modal | page
reacceptstring
grace_days | never | next_sign_in
requiredboolean
requireScrollboolean
showSummaryboolean
embedobject
Show child parameters
allowedOriginsstring[]
requireTicketboolean
tokenTtlSecondsnumber
enabledboolean
pageobject
Show child parameters
returnUrlsstring[]
policiesobject
readerobject
Show child parameters
promptstring
off | widget
uiobject
Show child parameters
groupingstring
combined | sequence
inheritThemeboolean
labelsobject
layoutstring
center | fullscreen | sheet
localestring
mobileFullscreenboolean
readerstring
modal | new_tab
varsobject
resolvedobjectrequired
Show child parameters
embedobjectrequired
Show child parameters
allowedOriginsstring[]required
requireTicketbooleanrequired
tokenTtlSecondsnumberrequired
enabledbooleanrequired
enabledSourcestringrequired
call | default | org | policy | space
pageobjectrequired
Show child parameters
returnUrlsstring[]required
readerobjectrequired
Show child parameters
promptstringrequired
off | widget
uiobjectrequired
Show child parameters
groupingstringrequired
combined | sequence
inheritThemebooleanrequired
labelsobjectrequired
layoutstringrequired
center | fullscreen | sheet
localestringrequired
mobileFullscreenbooleanrequired
readerstringrequired
modal | new_tab
varsobjectrequired
storedobjectrequired
Show child parameters
defaultsobject
Show child parameters
declinestring
allow | block
dismissibleboolean
eventsstring[]
graceDaysnumber
presentationstring
checkbox | checkbox-modal | inline-scroll | link | modal | page
reacceptstring
grace_days | never | next_sign_in
requiredboolean
requireScrollboolean
showSummaryboolean
embedobject
Show child parameters
allowedOriginsstring[]
requireTicketboolean
tokenTtlSecondsnumber
enabledboolean
pageobject
Show child parameters
returnUrlsstring[]
policiesobject
readerobject
Show child parameters
promptstring
off | widget
uiobject
Show child parameters
groupingstring
combined | sequence
inheritThemeboolean
labelsobject
layoutstring
center | fullscreen | sheet
localestring
mobileFullscreenboolean
readerstring
modal | new_tab
varsobject
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": [
        {}
      ]
    }
  },
  "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.

Same as 400 · PUT /orgs/{orgId}/policy-gate/config

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.

Same as 400 · PUT /orgs/{orgId}/policy-gate/config

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).

Same as 400 · PUT /orgs/{orgId}/policy-gate/config

422 The body was understood and a value is unacceptable (validation.failed). error.details names the field paths.

Same as 400 · PUT /orgs/{orgId}/policy-gate/config

429 Rate limited — see the RateLimit-* headers.

Same as 400 · PUT /orgs/{orgId}/policy-gate/config

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": [
        {}
      ]
    }
  },
  "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.
The Forumulate teamUpdated