Docs

Accounts

The admin users of the platform — invited by email, activated by the user, governed by exactly one role.

Overview

Invite-based admin users, each governed by exactly one role.

Organization accounts are the people who sign into the admin UI and manage roles, spaces, agents and the other org resources. Each account carries a globally unique email, optional first/last name (set by the user themselves when they register), exactly one role, and a status. What an account can see and do is entirely determined by its role — KeenAgents refuses to attach an account to a role that does not exist, so permissions always trace back to a real, current role. Passwords are stored irreversibly hashed and are never returned by any read.

Accounts are invite-based: an administrator creates one with just an email and a role, KeenAgents emails a confirmation link, and the invited user finishes registration themselves — setting their own name and password on the confirmation page. Every rule on this page is enforced by the platform itself; the admin UI mirrors the same rules early for a better experience, but they cannot be bypassed by calling the API directly.

Core Concepts

Account record
Reads return id, createdAt, updatedAt, lastLoginAt, email (unique), firstName/lastName (empty until the user confirms), role, confirmed and blocked. The credential is never included in any response, and the account list also omits blocked — only the single-account detail includes it.
Statuses
New — invited but not yet registered. Active — the user set their name and password via the emailed link. Blocked is a separate flag shown on the detail page. Status in lists and filters derives purely from whether the account is confirmed.
One role per account
The assigned role must always exist — an unknown role is refused with No such role!. Renaming a role follows through to every account holding it automatically. Deleting a role moves its accounts to the default role and force-logs-out any session still carrying the deleted role, so no session ever keeps permissions from a role that is gone.
Protected accounts
Every change is checked against a requester hierarchy: a requester with no role is refused; an owner requester may not act on the owner account; an admin requester may not act on owner or admin accounts (all answer Not allowed!). Your organization has exactly one owner, created at initial setup — even the owner cannot change the owner account's role, so the top of the hierarchy can never be accidentally demoted.
Emailed links
The confirmation and set-password pages require no login by design — the emailed link itself is the credential. Links are encrypted, expire after 24 hours (48 hours for the initial owner invitation) and are single-use: redeeming one invalidates it, and a newer invite or reset supersedes any older link. All failures answer the general 406 Action forbidden! so a failed attempt never reveals whether an email exists.
Reliability & audit
During platform maintenance an account operation may answer 503 — simply retry shortly. The automatic cleanup after a role deletion is never lost: it applies as soon as the platform is available again, even across a maintenance window. Every mutating action is recorded in the audit trail with both the requester's and the target account's identity.

The Account Lifecycle

From invite to Active — and what a password reset really does.

  1. An admin creates the account with an email and a role. KeenAgents checks the requester hierarchy, then stores the account unconfirmed — the emailed confirmation link is its only credential until the user registers.
  2. A confirmation email is dispatched with a link valid for 24 hours. The account shows New; its name fields stay empty until the user confirms.
  3. The user opens the link and submits first name, last name, password and password confirmation. KeenAgents validates the link against the invited account, stores the chosen password irreversibly hashed, saves the names and marks the account confirmed. A wrong or already-used link answers Action forbidden!.
  4. The account is now Active and can log in. Names are set by the user at confirmation and are not editable from the admin surface.
  5. A later admin-issued password reset invalidates the current password immediately — it stops working when the reset email is sent, not when the link is redeemed. The reset link accepts a new password only for a confirmed account.
Never reset a New account's password
On an unconfirmed account the pending confirmation link is the account's only credential. Issuing a reset replaces that credential — killing the emailed confirmation link — and the reset link itself refuses unconfirmed accounts, so the account is stuck until a fresh invite. The admin UI hides the Reset button on New accounts for exactly this reason; if you drive the API directly, do not issue a reset for a New account.
Recovering a locked-out admin
There is no self-serve "forgot password" on the login screen. A locked-out admin asks another admin — or the owner — to press Reset Password for them. Between that reset and opening the emailed link to set a new password, neither password works, so open the email right away. The owner account cannot be reset this way; the owner recovers through the initial setup path or support.

Signing In & Your Session

How signing in works day to day, and why a password prompt sometimes reappears.

You sign in with your email and password. For security your session is time-limited — after a stretch of inactivity a password prompt appears in place, right where you were working. Re-enter your password and you continue exactly where you left off: nothing you had open or in progress is lost.

  • The prompt is a security safeguard, not a preference — there is no toggle to turn it off.
  • It feels frequent in a sandbox on purpose: sandbox sessions are deliberately short. In production a session lasts much longer, so the prompt appears far less often.
  • Re-entering your password continues the current session in place — it is not a fresh login and does not send you back to the start of what you were doing.
You never lose your work to the prompt
The inactivity prompt re-authenticates you exactly where you were — it is a security re-check, not a reset. Fully signing out, or a session that has genuinely ended, is what returns you to the login screen instead.

Rules & Limits

  • Email — valid format, globally unique; a duplicate answers 403 This account is already created!. The admin UI caps length at 254 characters.
  • Role — required, must be an existing role; an unknown role answers No such role!. The protected targets are owner and admin; owner is never offered in any role select.
  • Registration — on the confirmation page, first name, last name, password and password confirmation are all required non-empty fields.
  • Paging — page defaults to 1 (0 is treated as 1); page size defaults to 20 and the items parameter overrides it with no enforced upper cap. Past-the-end pages clamp to the real last page, and the reply always reports the page you actually landed on — sync your pager off that, not off the page you asked for.
  • Filters — at most 10 filter rows per request; operators are equal | not | greater | less | like | not like | in | not in; values must be non-empty strings and in/not in take a comma-separated list. Anything off-vocabulary answers 406 Invalid filter!.
  • Sorting — fields name | mail | status | role | id | created at, directions asc | desc; off-vocabulary answers 406 Invalid sort!. Default order is email descending; name sorts by first name then last name with not-yet-named (New) accounts always at the bottom, and ordering is fully deterministic so pages never shuffle between requests.
Filter typeBehavior
nameCase-insensitive; matches either first or last name for positive operators, and requires neither part to match for negative ones — accounts that have no name yet (New) never match a negative name filter.
mail / id / roleCase-insensitive string matches; role is matched by its slug.
statusOnly active and new; operators equal | not | in | not in.
created at / created fromDay-granular UTC windows over the creation date; operators equal | not | greater | less.

account list URL grammar

<type>[.<operator>]=<value>     bare key = equal              name=jame
                                multi-word operators dot-join  name.not.like=jame
                                dated types use underscores    created_at, created_from
sort=<field>[.<direction>]      direction defaults to asc      sort=created_at.desc
page=<n>                        pager position                 page=2

In the Admin UI

  • Account List — a paged table (20/page) with sortable columns Name, Email, Role, Status, Created At and a Visit link per row (headers cycle asc → desc → off). Page, filters and sort all live in the URL — refresh, share and back/forward restore the exact view — and a page flip keeps the previous rows visible under a dim + spinner overlay.
  • Filter panel — repeatable rows of type + operator + value: free text for name/mail/id, fixed Active/New choices for status, live role choices (label = title, matched by slug) and a date picker for the created-at types. Apply is enabled only on a real change and always lands on page 1; the current sort survives a filter change and vice versa.
  • Create Account — email input (254-char cap) plus a role select fed by the live roles list excluding owner. Success toasts, notes that the confirmation email was sent, and redirects to the new account's detail page.
  • Account Details — header badges for role, Active/New status (green/purple), Blocked (red) and protection state: protected — read only for the owner, protected — role locked for admin accounts (role change locked, password reset and delete remain available). Email and names are read-only; the role select has a dirty-gated Save; Reset Password appears only on confirmed, non-owner accounts; delete is a two-step control hidden entirely for the owner.
  • Owner visibility — a non-owner requester never sees the owner account in the list; the row is excluded from both the results and the total count.

Good to Know

  • Role changes take effect on the next login (permissions are re-issued then), not instantly in an already-open session. Role deletion, by contrast, force-logs-out affected sessions.
  • Deleting an account immediately terminates any live session that account had — the delete and the logout are one action from your point of view.
  • The blocked flag renders as a red badge on the detail page, but no admin action currently sets or clears it.
  • In a hand-edited list URL, unknown filter keys and operators are silently ignored (a typo'd key simply does nothing), and only the first filter per type is honored.
  • Emailed links are invalidated by supersession: issuing a new reset or a fresh invite is what actually kills an older link — the newest link is always the only one that works.
Debugging a failed confirmation or delete
Errors on sensitive and public paths are deliberately general — Action forbidden!, Not allowed!, bare 406s — revealing the failure class, never details such as whether an email exists; this keeps probing attackers blind. Check the platform's audit log instead of expecting the API message to explain.

Previous

Platform Overview

Next

Roles & Permissions

Keen Agents 2026

Documentation

Release 15