← Back to blog

Who can do what in your okokumo team

2026-11-02

engineeringsecurity

When you sign up, okokumo creates an organization and makes you its owner. Anyone you invite after that joins as an owner, an admin or a member. This post lists what each of those roles can do and what happens at the edges: the last owner, an expired invitation, a teammate who leaves. Every rule here is checked in the Go handler for the route, so it applies the same way in the dashboard, over curl, and through an AI assistant.

The matrix

Action Owner Admin Member
See members and pending invitations ✓ ✓ ✓
Invite people, revoke invitations ✓ ✓ —
Change roles, remove members ✓ ✓ —
Rename the organization ✓ — —
Start a subscription, open the Stripe portal ✓ ✓ —
Create or revoke API keys ✓ ✓ —
Approve or disconnect apps (OAuth, MCP) ✓ ✓ —
Export the whole organization ✓ ✓ —
Export your own data ✓ ✓ ✓
Edit the status page, its components and domain ✓ ✓ —
Post, update or delete status-page incidents ✓ ✓ —
Run a check now, start or end maintenance ✓ ✓ —
Create, edit, pause, delete and import checks ✓ ✓ ✓
Create, edit, test and delete alert channels ✓ ✓ ✓

Members can read everything in the list: the plan, the API key list (names and prefixes, never the keys), the status page and its incidents.

Note the last two rows. A member is not a read-only seat. Members can create, change and delete checks and alert channels, but they can't post anything to the public status page. The roles separate who runs the account from who does the monitoring work. They do not separate read from write. If you want someone to look but not touch, okokumo has no role for that today.

What a member actually sees

The Team page shows the role dropdown and the Remove button to everyone. The server decides, and the page prints its answer. A member who tries to promote themselves gets:

HTTP/1.1 403 Forbidden

{"message":"only owners and admins manage team members"}

Each area has its own sentence, so the error says what was refused:

  • Inviting: only owners and admins invite team members
  • Billing: only owners and admins manage billing
  • API keys: Only owners and admins manage API keys
  • Connecting an app: Only owners and admins can connect apps
  • Organization export: only owners and admins export organization data
  • Status page: only owners and admins can manage the status page
  • Run now: only owners and admins can run a check

The same check runs no matter how the request arrives. An API key acts as the person who created it, with that person's role at the time of the request, because the membership is loaded again on every call. If an admin is demoted to member, their keys can no longer post incidents from the next request on. The MCP server sends each tool call through the same /v1 routes, and a test asserts that a member's assistant gets the same 403 on create_incident that curl gets.

Some routes don't accept an API key at all, whatever the role: team management, billing, API key management and both data exports require a signed-in browser session. A leaked key can't invite anyone or create another key.

The last owner can't leave

An organization always keeps at least one owner. Two handlers enforce it. When a role change would turn an owner into something else, or a removal would delete an owner, the handler first counts the owners. If there is only one, the request stops with:

HTTP/1.1 409 Conflict

{"message":"organization must keep at least one owner"}

There's no separate Leave organization button. You leave by being removed on the Team page, and owners and admins can remove themselves. So a sole owner who presses Remove on their own row gets the 409 above, and so does an attempt to change their own role to admin. The way out is to promote someone else to owner first. Once there are two, either one can step down or leave. Members can't remove anyone, themselves included, so a member who wants out asks an owner or admin.

Both cases are covered in the team package's tests: TestUpdateMemberRoleBlocksLastOwnerDemotion and TestRemoveMemberBlocksLastOwner.

Admin is one step from owner

This deserves a plain statement. The manager check on the Team routes is "not a member", so admins and owners pass it alike. An admin can give anyone the owner role, themselves included, and can demote or remove another owner as long as one remains. The only action reserved for owners is the rename below.

In practice, make someone an admin only if you would also trust them as owner. Admin is not a limited role in this model.

Invitations

Inviting someone creates a token from 24 bytes of crypto/rand, hex-encoded to 48 characters. The database stores only its SHA-256 hash. The raw token lives in the link we email, /invite/<token>. The dashboard also shows that link once, right after you send the invitation, for when email doesn't arrive. We can't rebuild a link from what we store, and the organization export lists pending invitations without the hash.

When someone opens the link, the server hashes the token and looks for a row with that hash and an expires_at still in the future. The expiry is set to exactly seven days after the invitation was created, and the email says so. An unknown token and an expired one get the same answer:

HTTP/1.1 404 Not Found

{"message":"invalid or expired invitation"}

That's what production returned for a made-up token while we wrote this post.

Some other rules on that path:

  • One live link per address. Inviting the same email again deletes the earlier invitation in the same transaction, so the older link stops working. A unique index on organization and email backs this up.
  • Single use. Accepting creates the account and its membership with the invited role, then deletes the invitation, all in one transaction.
  • The inbox is the proof. The invitee chooses a password, at least 10 characters, and the account starts with its email already verified, because following the link proved they control that address.
  • One organization per account. An account belongs to exactly one organization for now. Inviting an address that already has an okokumo account fails straight away with 409 an account with this email already exists, so you don't send a link that could never be accepted.

Revoking an invitation from the Team page deletes the row, and the link then gets the same 404.

Removing someone

Removing a member deletes their membership. Any apps they connected to the organization through OAuth are revoked at the same time, not the next time those apps fail a lookup. API keys they created stop working too: a key authenticates as its creator's membership in its own organization, and once that membership is gone the request gets 401 No organization membership. The rows still appear in the key list, so revoke them as well to keep the list accurate.

Renaming is owner-only

PUT /v1/organization is the one action admins can't take:

HTTP/1.1 403 Forbidden

{"message":"only the owner can rename the organization"}

The name is trimmed, must not be empty, and can be up to 100 characters. It appears in the header of every teammate's checks page, in the subject of invitation emails, on the page an invitee sees ("Join Acme") and on the consent screen when someone connects an app ("Connect an app to Acme?"). It's what your team and the people you invite use to recognise the account, so we treat it as an owner decision. Nothing keys off the name, so renaming has no other effect.

The membership and invitation rules live in the API's team package, about 400 lines plus tests. You'll find the roles under Account → Team in the dashboard. If a rule here gets in your way, or you need a role this model doesn't have, tell us.