Wocha Docs

Organisation security policy API

Customer API endpoints for per-organisation and tenant password security policies

Manage organisation security policies and tenant password defaults programmatically. All endpoints require a management API key or OAuth token with the appropriate scopes.


Authentication

APITokenBase URL
Customer APIManagement API key (wocha_mgmt_...) or OAuth tokenhttps://TENANT.api.wocha.ai/v1/customer
EndpointRequired scope
GET .../security-policyorganisations:read
PUT / DELETE .../security-policyorganisations:write
GET / PUT .../password-policysettings:read / settings:write

Organisation security policy

Get effective policy

Returns the merged effective policy for an organisation, with _inherited flags indicating which fields use tenant defaults versus org overrides.

GET /v1/customer/organisations/{orgId}/security-policy
Authorization: Bearer wocha_mgmt_...

Response 200 OK:

{
  "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
  "org_id": "org_acme_engineering",
  "mfaEnforcement": "required",
  "mfaPolicy": "adaptive",
  "allowedFactors": ["totp", "webauthn"],
  "preferredFactor": "webauthn",
  "gracePeriodMs": 86400000,
  "unenrolledUserAction": "force_enrollment",
  "passwordMinLength": 14,
  "passwordRequireUppercase": true,
  "passwordRequireLowercase": false,
  "passwordRequireNumber": true,
  "passwordRequireSymbol": false,
  "passwordHibpCheck": true,
  "passwordMaxAgeDays": 90,
  "passwordDisallowReuse": 5,
  "sessionTimeoutMinutes": 60,
  "sessionIdleTimeoutMinutes": 15,
  "requirePhoneVerification": false,
  "_inherited": {
    "mfaEnforcement": false,
    "mfaPolicy": true,
    "allowedFactors": true,
    "preferredFactor": true,
    "gracePeriodMs": true,
    "unenrolledUserAction": true,
    "passwordMinLength": false,
    "passwordRequireUppercase": false,
    "passwordRequireLowercase": true,
    "passwordRequireNumber": false,
    "passwordRequireSymbol": true,
    "passwordHibpCheck": true,
    "passwordMaxAgeDays": false,
    "passwordDisallowReuse": false,
    "sessionTimeoutMinutes": false,
    "sessionIdleTimeoutMinutes": false
  }
}

_inherited field

Each key in _inherited is a boolean:

ValueMeaning
trueField is inherited from the tenant default (no org override)
falseField is overridden at the organisation level

Use _inherited to build UIs that show inherit/override state without comparing against tenant defaults client-side.

Errors:

StatusCodeWhen
404not_foundOrganisation does not exist
401unauthorizedInvalid or missing token
403forbiddenToken lacks organisations:read scope

Update org overrides

Set organisation-specific overrides. Only include fields you want to change. Pass null to clear an override and revert to tenant inheritance.

PUT /v1/customer/organisations/{orgId}/security-policy
Authorization: Bearer wocha_mgmt_...
Content-Type: application/json
 
{
  "mfa_enforcement": "required",
  "mfa_policy": "always",
  "allowed_factors": ["totp", "webauthn"],
  "password_min_length": 16,
  "password_max_age_days": 90,
  "session_timeout_minutes": 45,
  "session_idle_timeout_minutes": 10
}

CamelCase field names are also accepted (mfaEnforcement, mfaPolicy, etc.).

Validation rules:

  • mfaEnforcement cannot be less strict than the tenant default
  • mfaPolicy cannot be less strict than the tenant MFA policy
  • allowedFactors must be a subset of tenant allowed factors
  • passwordMinLength cannot be less than the tenant minimum
  • Password complexity and HIBP flags cannot be disabled when required by tenant
  • passwordMaxAgeDays cannot exceed the tenant maximum age
  • passwordDisallowReuse cannot be less than the tenant reuse limit
  • sessionTimeoutMinutes ≥ 5, sessionIdleTimeoutMinutes ≥ 1

Response 200 OK: Returns the updated effective policy (same shape as GET).

Errors:

StatusCodeWhen
400validation_errorPolicy violates strictness rules
404not_foundOrganisation does not exist

Reset to tenant defaults

Removes all organisation-specific overrides from org_security_policies.

DELETE /v1/customer/organisations/{orgId}/security-policy
Authorization: Bearer wocha_mgmt_...

Response 200 OK: Returns the effective policy after reset (all _inherited flags true where no legacy org_metadata overrides exist).


Tenant password policy

Configure the tenant-wide password default. All organisations inherit these values unless they override specific fields.

Get tenant password policy

GET /v1/customer/settings/password-policy
Authorization: Bearer wocha_mgmt_...

Response 200 OK:

{
  "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
  "minLength": 12,
  "requireUppercase": false,
  "requireLowercase": false,
  "requireNumber": false,
  "requireSymbol": false,
  "hibpCheck": true,
  "maxAgeDays": 0,
  "disallowReuse": 0
}

Update tenant password policy

PUT /v1/customer/settings/password-policy
Authorization: Bearer wocha_mgmt_...
Content-Type: application/json
 
{
  "min_length": 14,
  "require_uppercase": true,
  "require_number": true,
  "hibp_check": true,
  "max_age_days": 180,
  "disallow_reuse": 8
}

Snake_case and camelCase field names are both accepted.

FieldTypeConstraints
minLength / min_lengthinteger8–128
requireUppercase / require_uppercaseboolean
requireLowercase / require_lowercaseboolean
requireNumber / require_numberboolean
requireSymbol / require_symbolboolean
hibpCheck / hibp_checkboolean
maxAgeDays / max_age_daysinteger≥ 0 (0 = no expiry)
disallowReuse / disallow_reuseinteger≥ 0 (0 = disabled)

Response 200 OK: Returns the updated policy (same shape as GET).


Code examples

cURL — require MFA for an engineering org

curl -X PUT "https://acme.api.wocha.ai/v1/customer/organisations/org_eng/security-policy" \
  -H "Authorization: Bearer wocha_mgmt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mfaEnforcement": "required",
    "mfaPolicy": "always",
    "allowedFactors": ["totp", "webauthn"]
  }'

JavaScript — read effective policy and check inheritance

async function getOrgPolicy(orgId) {
  const res = await fetch(
    `https://acme.api.wocha.ai/v1/customer/organisations/${orgId}/security-policy`,
    { headers: { Authorization: `Bearer ${process.env.WOCHA_MGMT_KEY}` } },
  );
 
  if (!res.ok) throw new Error(`API error: ${res.status}`);
  return res.json();
}
 
const policy = await getOrgPolicy("org_eng");
 
if (!policy._inherited.mfaEnforcement) {
  console.log(`Custom MFA enforcement: ${policy.mfaEnforcement}`);
}
 
if (policy.passwordMaxAgeDays > 0) {
  console.log(`Password rotation every ${policy.passwordMaxAgeDays} days`);
}

JavaScript — update tenant password policy

await fetch("https://acme.api.wocha.ai/v1/customer/settings/password-policy", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.WOCHA_MGMT_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    minLength: 14,
    requireUppercase: true,
    hibpCheck: true,
    maxAgeDays: 90,
    disallowReuse: 5,
  }),
});