The Tenant API exposes a single per-tenant configuration object covering password policy, login behavior, passwordless authentication, captcha, and rate limits. It is read via the configuration query and written via the configure mutation.

Every successful configure call is recorded in the audit log as a tenant_config_change event, with captcha.secret redacted to ***.

Reading configuration

query {
  configuration {
    signup {
      requireEmailVerification
    }
    emailChange {
      requireVerification
    }
    password {
      minLength
      requireUppercase
      requireLowercase
      requireDigit
      requireSpecial
      pattern
      checkBlacklist
      checkHibp
    }
    login {
      revealUserExists
      revealLoginMethod
      baseBackoff
      maxBackoff
      attemptWindow
      defaultTokenExpiration
      maxTokenExpiration
    }
    passwordless {
      enabled
      url
      expiration
    }
    captcha {
      provider
      threshold
      protect { signUp passwordReset passwordlessInit emailVerification }
    }
    rateLimits {
      signUpPerIp        { limit window }
      loginPerIp         { limit window }
      passwordResetPerIp { limit window }
      passwordlessInitPerIp { limit window }
      emailVerificationPerIp { limit window }
    }
  }
}

The captcha.secret is intentionally not exposed by the schema — it is a credential, write-only.

Writing configuration

mutation {
  configure(config: {
    password: { minLength: 12, checkHibp: true, checkBlacklist: true },
    login: { revealUserExists: false, defaultTokenExpiration: "PT30M" },
    captcha: { provider: turnstile, secret: "0x4AAA…" },
    rateLimits: {
      signUpPerIp:           { limit: 5,  window: "PT1H" },
      loginPerIp:            { limit: 20, window: "PT1H" },
      passwordResetPerIp:    { limit: 5,  window: "PT1H" },
      passwordlessInitPerIp: { limit: 5,  window: "PT1H" },
      emailVerificationPerIp: { limit: 5, window: "PT1H" }
    }
  }) {
    ok
    error { code developerMessage }
  }
}

All ConfigInput fields are optional and partial — unset fields preserve the current value.

Requires the CONFIGURE tenant permission. By default this is held by SUPER_ADMIN.

Tip

Prefer keeping configuration in version control? Describe it in a typed tenant.config.ts and apply it with the CLI — see declarative configuration.

Sections

signup (since 2.2)

FieldDefaultNotes
requireEmailVerificationfalseNew accounts must verify their e-mail before they can sign in. Captured per account at sign-up; toggling affects only accounts created afterwards. See e-mail verification.

emailChange (since 2.2)

FieldDefaultNotes
requireVerificationfalseSelf-service changeMyProfile e-mail changes go through confirmEmailChange against a token mailed to the new address, instead of swapping immediately. Independent of signup.requireEmailVerification. See e-mail verification.

password

See password policy for individual fields and the WeakPasswordReason enum returned with TOO_WEAK errors.

FieldDefaultNotes
minLength8
requireUppercase, requireLowercase, requireDigit, requireSpecial0Minimum count of each character class
patternnullOptional regex; pattern violations yield INVALID_PATTERN
checkBlacklisttrue10k-most-common list with leetspeak normalization
checkHibpfalseHIBP k-anonymity check; opt-in. See anti-abuse. Available since 2.2.

login

FieldDefaultNotes
revealUserExiststrueWhen false, unknown-email failures on sign-in / reset / passwordless-init are masked. See anti-abuse — enumeration protection.
revealLoginMethodtrue(since 2.2) When false, signIn collapses NO_PASSWORD_SET / INVALID_PASSWORD into a generic INVALID_CREDENTIALS and signUp omits the recommendedAction hint on EMAIL_ALREADY_EXISTS. Orthogonal to revealUserExistsUNKNOWN_EMAIL is still controlled by that flag.
baseBackoffPT1SStarting backoff between per-email login attempts. Also drives the per-email mail-init throttle for password reset and passwordless init.
maxBackoffPT1MUpper bound for the exponential backoff.
attemptWindowPT5MHow long failed attempts are remembered.
defaultTokenExpirationPT30MSession token lifetime when the client omits expiration.
maxTokenExpiration6 monthsHard cap on client-requested expiration.

Intervals follow ISO 8601 duration syntax (PT5M, PT1H, P1D, …).

passwordless

See the passwordless page.

captcha (since 2.2)

See anti-abuse — captcha.

FieldNotes
providerturnstile, hcaptcha, or recaptchaV3. null disables captcha.
secretWrite-only. The provider's server-side secret. Encrypted at rest with the tenant's Providers keychain. Passing null leaves the stored value unchanged; passing "" clears it.
thresholdreCAPTCHA v3 score threshold (0.0–1.0). Ignored for hCaptcha / Turnstile.
protectPer-flow enforcement. The provider/secret is shared; these flags pick which mutations require a token. signUp / passwordReset / passwordlessInit default true; emailVerification defaults false (opt in if requestEmailVerification is publicly exposed). Has no effect while provider is null.

rateLimits (since 2.2)

Sliding-window per-IP limits. limit: 0 (the default) disables that scope. See anti-abuse — rate limits.

ScopeApplied to
signUpPerIpsignUp
loginPerIpsignIn, signInIDP, signInPasswordless
passwordResetPerIpcreateResetPasswordRequest
passwordlessInitPerIpinitSignInPasswordless
emailVerificationPerIprequestEmailVerification
mutation {
  configure(config: {
    password: { minLength: 12, checkBlacklist: true, checkHibp: true },
    login:    { revealUserExists: false, revealLoginMethod: false },
    captcha:  { provider: turnstile, secret: "" },
    rateLimits: {
      signUpPerIp:           { limit: 5,   window: "PT1H" },
      loginPerIp:            { limit: 20,  window: "PT1H" },
      passwordResetPerIp:    { limit: 5,   window: "PT1H" },
      passwordlessInitPerIp: { limit: 5,   window: "PT1H" },
      emailVerificationPerIp: { limit: 5,  window: "PT1H" }
    }
  }) { ok error { code developerMessage } }
}

For most tenants this is enough to make brute-force, password-spray, and enumeration attacks impractical without affecting legitimate users.