Create or replace an organization policy

PUT /v1/organizations/{org_id}/policies/{policy_kind} · permission organizations:write

Replaces the whole settings object of one policy kind. An organization policy can only be stricter than the environment's setting. The plan gate is enforced by the server: the session and ip_allowlist kinds and the AAGUID allowlist require Enterprise (open in development environments). The settings content is written to the management audit log; the organization.updated event does not carry it.

Request

Field Type Required Description
org_id path yes Organization ID (org_…).
policy_kind path yes Policy kind: authentication_methods, mfa, phishing_resistant, sso_enforcement, session or ip_allowlist. Any other value returns 404.
If-Match header no Required when the policy exists (ETag of the last read); omit it to create the policy. * matches an existing policy.
settings object yes The kind's settings object. Fields by kind: authentication_methods: allowed_connection_ids (conn_…), allow_passkey, allow_password; mfa: required_for (none, admins, all), allowed_factors (totp, webauthn, sms, email), enrollment_grace_days (0-30); phishing_resistant: required_for, aaguid_allowlist (at most 500); sso_enforcement: mode (off, org_context, verified_domains), connection_ids (conn_…), bypass_user_ids (usr_…, at most 2); session: idle_minutes, absolute_minutes, refresh_idle_days, refresh_absolute_days; ip_allowlist: cidrs (at most 100), applies_to (admins, all).

Request body:

{
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  }
}

Response

201 Created when the policy was created, 200 OK when it was replaced; the body is the policy and the response carries its new ETag.

{
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "policy_kind": "mfa",
  "schema_version": 1,
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  },
  "updated_at": "2026-10-12T09:30:00.123456Z",
  "version": 1
}

Errors

HTTP code When
400 unknown_field The body has a field that is not in the schema or is read-only.
403 plan_feature_unavailable The kind or the AAGUID allowlist requires a higher plan; the problem carries feature and required_plan.
404 not_found The organization does not exist or the kind is unknown.
409 domain_not_verified sso_enforcement with mode verified_domains needs a verified domain on the organization.
409 connection_test_required Turning sso_enforcement on (from off or from no policy) needs a successful test sign-in of the current configuration of at least one of the connection_ids in the last 24 hours. Run a connection test (POST /v1/connections/{connection_id}:test) and try again.
409 organization_pending_deletion The organization is waiting for self-service deletion; cancel the deletion first.
412 precondition_failed If-Match does not match the current version, or was sent while no policy of this kind exists, or another request created the policy while this request was creating it; read the policy again.
422 validation_failed A settings field is unknown or invalid; errors[] points at /settings/….
422 policy_weaker_than_parent The setting is weaker than the environment's setting; errors[] points at the weaker field.
422 at_least_one_method authentication_methods leaves no sign-in method.
428 precondition_required If-Match is missing while the policy exists.
401 unauthenticated The credential is missing, invalid or revoked.
403 insufficient_permissions The credential lacks the required permission.
429 rate_limited The rate limit was exceeded; retry after the Retry-After period.
503 service_unavailable A dependency is temporarily unavailable; retry after the Retry-After period.

Example

curl -X PUT https://api.eu.keenauth.com/v1/organizations/org_01j9xk3m7q8r2s4t6v8w0y2z4a/policies/mfa \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"required_for":"all","allowed_factors":["totp","webauthn"]}}'