disablePerson disables a person account. Once disabled, the person can no longer sign in via any flow — password, IDP, or passwordless. Existing sessions are not revoked by this mutation; pair it with forceSignOutPerson if you also need to end the person's active sessions.
mutation {
disablePerson(personId: "…") {
ok
error { code developerMessage }
}
}
Errors:
| Code | Cause |
|---|---|
PERSON_NOT_FOUND | No person with that id. |
PERSON_ALREADY_DISABLED | The person is already disabled. |
Permissions
Gated by PERSON_DISABLE against the target's roles:
SUPER_ADMIN— can disable any person.PROJECT_ADMIN— can disable persons whose roles do not exceed the admin's own (the role-escalation guard).- Anyone with the tenant role explicitly granted
PERSON_DISABLEpermission — see Tenant ACL permissions.
forceSignOutPerson uses the same role-escalation rule.
Common usage pattern
To fully cut access for a compromised account:
mutation {
disablePerson(personId: "…") { ok error { code } }
forceSignOutPerson(personId: "…",
reason: "Compromised") { ok error { code } }
}
disablePerson blocks future sign-ins; forceSignOutPerson invalidates every existing API key (session and permanent) for the person and sends them a FORCED_SIGN_OUT mail.
Re-enabling
enablePerson is the exact inverse of disablePerson — it clears the disabled flag so the person can sign in again:
mutation {
enablePerson(personId: "…") {
ok
error { code developerMessage }
}
}
Errors:
| Code | Cause |
|---|---|
PERSON_NOT_FOUND | No person with that id. |
PERSON_ALREADY_ENABLED | The person is not disabled. |
Enabling only lifts the sign-in block. API keys invalidated while the account was disabled stay invalidated, so the person has to sign in again.
Permissions are the same as for disabling — enablePerson is gated by the very same PERSON_DISABLE action against the target's roles, so anyone who may disable a person may also re-enable them. There is no separate permission to grant.
Reading the disabled state
Person.disabledAt exposes the state so a management UI can show it and offer the right action:
query {
personById(id: "…") {
id
email
disabledAt
}
}
It is the timestamp of the disabling, or null while the account is active. The field is read-only — change it with disablePerson / enablePerson. It is queryable wherever a Person is reachable — me, personById, the persons listing, and projectBySlug { members { identity { person } } } — and needs no permission beyond the one that makes the Person visible in the first place.
Audit
Both mutations record an entry in the audit log with personId set to the target — disablePerson records person_disable, enablePerson records person_enable (since 2.2). A failed attempt against an existing person is recorded too, so a repeated PERSON_ALREADY_ENABLED shows up in the trail.