Skip to content
BeeuniBeeuni
English
Esc
↑↓navigate↵open⌘Jpreview
On this page

Accounts & access

An account holds your projects, your teammates, and one access model.

An account holds your projects and the people who work in them. When you sign up, Beeuni creates a personal account for you. Invite a teammate and it becomes a team account. Personal and team accounts use the same roles, billing, and limits.

One access model

Beeuni has one grant record: an assignment. Every assignment binds one principal to one role, at one scope.

Term Meaning
Principal Who holds the access. A user, a group, a service_account (an agent’s identity), or a pending invitee email.
Role A named set of permissions. Beeuni ships the built-in roles below. An account can add custom roles.
Permission One action, for example project.secret.read or member.update.
Scope Where the role applies: the whole account, or one project.
Object Optional. Narrows the assignment to one agent, skill, secret, app, or trigger inside the scope.
Expiry Optional. The assignment stops granting at expires_at.

There is no second grant store. A group’s access, a per-resource grant, and a custom-role binding are all assignments. They differ only in the principal, the object, and the role.

Account roles

Each person in an account holds one account role:

  • Owner — full control, including members and billing.
  • Admin — manages projects, members, groups, roles, and tokens.
  • Member — works only in the projects they hold a project assignment on.

Owner and admin hold manager-equivalent access on every project in the account. A project assignment cannot lower that. To limit an owner or an admin on one project, first change their account role to member.

Manager-equivalent access does not include members’ private sessions. An account owner can turn that on for owners and admins with Admins can open every session under Settings → Security. Only an owner can change it, and every use is audited. See Session access.

Invite a teammate by email from the account’s members page. The invitation is an assignment on a pending principal. It becomes a user assignment when they accept.

Project roles

Inside a project, a principal holds one of two roles:

  • Member — reads the project, starts and stops its sessions, fires its triggers. No editing, no configuration.
  • Manager — every project permission: edits the project, manages triggers, connectors, skills, secrets and access, holds the gateway keys, and deletes the project.

There is no third project role.

Manager is the full set of project permissions. A custom role only adds permissions, so no role can withhold one from a manager. Object assignments are the separate mechanism that narrows which objects a permission reaches.

Set project access from the project’s access settings, or with beeuni access — see CLI.

Groups

A group is a principal, exactly like a person. Assign a role to a group at a scope and every member of that group holds it. A group adds access. It never removes access a person already holds. A group provisioned by SCIM is the same principal type as one you create by hand.

Object assignments

An assignment can name one object inside a project: an agent, a skill, a secret, an app, or a trigger. Beeuni enforces object assignments on agents and skills today.

  • Agents are closed by default. A member reaches an agent only when an assignment names them, one of their groups, or everyone in the project.
  • Every other object type is open by default. With no assignment on the object, a member reaches it. A shared connector account with an assignment is usable only by the people it names — see Choose who can use a shared account.
  • Everyone in the project is a principal too. In the Grant access dialog, pick Everyone in followed by the project’s name to give an agent to every member at once (principal_type: "project", principal_id = the project id). It holds object assignments only — never a role — so each person keeps their own project role.
  • An object assignment restricts a project manager too. That is what makes “scope this agent to the finance group” mean anything. What the manager role buys is the unscoped default, not an exemption.
  • Account owners and admins are never restricted by an object assignment, and neither is a service account acting on its own assignments.

An object assignment carries no permissions of its own. It answers “which objects”, not “which actions”.

Expiry

Give an assignment an expires_at and it stops granting at that instant. The engine ignores an expired assignment. The row stays for the audit trail.

One vocabulary, two bindings

A person, a group, or an agent gets access from Beeuni as roles — the assignments described above. An agent carries a second, separate binding: the Beeuni permissions its manifest declares in beeuni.yaml under agents.<name>.beeuni_permissions. A session can only do what both allow. The two never widen each other. An agent whose manifest lists project.secret.read still reads nothing when the role verdict denies it, and an agent launched by an owner still reads nothing when its manifest does not list the action. Roles are account state; Beeuni permissions are repository state in the manifest. See Manifest reference.

The role that counts for an agent session is the role bound to the agent’s own service account — its ceiling — or the default agent ceiling when none is bound. The role of the person who started the session does not apply. One permission, project.credentials.issue, is human-only and never reaches an agent. An agent grants only what it holds. Admins pick an agent as the principal of a project role in the Access hub, under Agents. See Agent permissions.

Custom roles

A custom role is an account-owned role with the permissions you choose. Assigning one writes a single assignment — there is no built-in baseline row beneath it. A custom role can grant project access with no built-in project role at all, which is how a department-style role works.

A custom role only adds permissions. Beeuni has no deny rule.

Custom roles need the enterprise rbac entitlement. Without it, assigning one answers 402 with code: "entitlement_required".

Super-admin

Super-admin is not a role. It is a flag on one account membership. It bypasses every permission check, and every bypass is written to the audit log. Roles are the mechanism for ordinary access; super-admin is the audited escape hatch.

The account creator starts as owner and super-admin. When an owner is demoted, the owner role and the super-admin flag go together: the change clears the flag and records iam.member.super_admin.revoke. A super-admin flag granted to someone who is not an owner stays until an owner revokes it.

Single sign-on domain

A SAML identity provider asserts each person’s email address. Beeuni trusts that address outside your account only for a domain your account has verified. Until then:

  • a person signed in through your identity provider still joins your account and gets their groups;
  • their SSO identity matches no invitation from another account, and another account’s “add member by email” does not find it;
  • SSO sign-in never merges an existing identity into the SSO identity;
  • Enforce SSO has no effect, and password sign-in keeps working for the domain.

To verify the domain, open Identity in account settings. The SSO card shows a DNS TXT record: name _beeuni-verification.<your-domain>, value beeuni-verification=<token>. Publish it at your DNS provider, then select Verify domain (POST /v1/accounts/:accountId/iam/sso/provider/verify-domain). One domain belongs to one account: a domain that another account has verified cannot be verified again. Changing the primary domain starts a new, unverified claim.

With a verified domain and Enforce SSO on, the web sign-in form, /v1/access/check-email, and the headless /v1/auth/sign-in/password, /v1/auth/sign-in/magic-link and /v1/auth/verify-otp routes all refuse the password and email-code paths for that domain (403 sso_required).

SSO sign-in never merges an owner or a super-admin into an SSO identity, even on a verified domain. They keep their existing identity, and the SSO identity joins as a separate member.

Per-feature access settings

Some features carry their own visibility setting on top of the role model:

Setting Decides
App access mode Who can open one deployed App
Trigger session access Who can open the sessions one trigger creates
Slack channel policy Who can start a session from one channel
Computer capabilities Which filesystem, shell, and desktop calls a connected computer accepts

Each one narrows access to one resource. None of them grants a permission the role verdict denies.

The access API

Method + path Does
GET /v1/accounts/{accountId}/iam/assignments List assignments. Filter by principal, scope, object, or role.
POST /v1/accounts/{accountId}/iam/assignments Create one assignment.
DELETE /v1/accounts/{accountId}/iam/assignments/{assignmentId} Revoke one assignment.
GET /v1/accounts/{accountId}/iam/permissions The permission catalog, as data.
GET /v1/accounts/{accountId}/iam/roles Roles, built-in and custom.
GET /v1/accounts/{accountId}/iam/roles/{roleId}/permissions One role’s permissions.

The catalog is data, not a hardcoded list. Each permission carries its action, scope_type, resource_type, delegable flag, description, area, level, and implies. Read it instead of hardcoding action strings.

The write routes choose the permission they require from what you are granting: project.members.manage for a project role or an object assignment, project.connector.connections.manage for a connection assignment, member.update for an account role, policy.create for a custom role. You cannot side-step a ceiling by picking a different route.

Branding

An Enterprise account can put its own brand on the app for every member: a wide logo, a square icon, and a favicon, each with an optional dark-mode variant, plus a product name that replaces “Beeuni” in the browser tab title. Open Account → Branding (/accounts/{accountId}?tab=branding). Uploads accept PNG, JPEG, WebP, SVG, and ICO up to 1 MB; the icon stands in for a missing logo, the favicon falls back to the icon, and a missing dark variant falls back to the light image. In-app marks follow the app theme; the favicon follows the operating system’s color scheme.

Branding follows the account, not the browser: inside a project, members see the brand of the account that owns the project. Sign-in pages, emails, and public share pages stay Beeuni, because there is no account to brand until someone is signed in. When the Enterprise entitlement lapses, members see Beeuni again — nothing is deleted, and the account can remove what it uploaded at any time.

Method + path Does
GET /v1/accounts/{accountId}/branding The stored record and whether the plan allows it.
PUT /v1/accounts/{accountId}/branding Set or clear app_name. Needs account.write and the branding entitlement (402 entitlement_required otherwise).
POST /v1/accounts/{accountId}/branding/assets/{kind} Upload one image as multipart file. kind is logo, icon, favicon, or their _dark variants. Same gate as PUT.
DELETE /v1/accounts/{accountId}/branding/assets/{kind} Remove one image. account.write only.
DELETE /v1/accounts/{accountId}/branding Reset everything to Beeuni. account.write only.

GET /v1/accounts carries each account’s effective branding — the record while entitled, null otherwise — so a client renders from one request it already makes. In the SDK: beeuni.accounts.branding.{get, update, uploadAsset, removeAsset, reset}.

Switching accounts

If you belong to more than one account, switch between them from the account switcher. Each account keeps its own projects, members, and settings.

Signed-in devices

Settings → Security → Devices lists every place you are signed in: browsers, the desktop app, and the mobile app.

  • Sign out on one device ends that sign-in at once. It cannot refresh, its token stops working on account routes, and a phone or a browser stops getting push notifications.
  • Sign out other devices ends every other sign-in. None of them can refresh, and their phones and browsers stop getting push notifications at once.

A phone or a browser that signs in again gets push notifications again. A browser gets them only from projects with notifications on.

Tokens

Beeuni signs you in with a personal access token (beeuni_pat_...). It acts as the user who created it and holds exactly that user’s assignments. A service account (beeuni_sa_...) is a separate principal, not a person’s credential, and holds no project access until an assignment gives it some.

The two live on two surfaces, because they belong to two different owners. Your own API keys are in your settings, at Settings → API keys (/settings/tokens) — only you see them, and they stop working when your membership does. Service account tokens are account configuration, at Account → Tokens, beside the key rules that govern expiry.

See SDK authentication for the full token model.

Audit log

The audit log records every request that reaches the API, whether or not the request identified its caller. The server writes one row per HTTP request, WebSocket upgrade, preview request, Git transfer, and SCIM call. Coverage does not depend on the route.

Each row names the caller that the request proved:

actor_type Caller
human A signed-in person, or a person’s token (source: api_key).
agent A session’s agent credential. The row also names the human the agent acts on behalf of.
service_account A service account token.
system An account API key, a SCIM directory token, an integration webhook with a verified signature, or a Beeuni background job (source: worker).
anonymous A request with no credential, or with an invalid or forged one.

Each row also records the credential the API authenticated, in credential_kind and credential_id. The id names the credential, never the secret. The web app shows it as Via, with the token or app name where one exists.

credential_kind Credential credential_id
browser_session A Beeuni sign-in session (the web app, or any client holding a sign-in JWT). The sign-in session id.
personal_access_token A personal access token, such as the CLI’s. The token id.
oauth_app A connected app’s beeuni_oat_ token, such as an MCP client. The OAuth client id.
session_token An agent session’s own token. The session id.
api_key An account API key. The key id.
service_account A service account token. The service account id.
scim_token A SCIM directory token. The token id.

The API never reads X-Beeuni-Client. The CLI, the MCP server, and the web app all call the same API, and any client can send any header value, so the log records what the API can prove. Rows written before 2026-09-30 may carry a self-reported client_reported_source. New rows leave it empty. Filter by credential with ?credential_kind=oauth_app on GET /v1/accounts/:accountId/audit, or beeuni audit ls --credential-kind oauth_app.

Rows written before 2026-10 carry session_sequence, integrity_previous_hash, and integrity_hash: a per-session counter and a hash chain. New rows leave all three empty, and the fields are deprecated. Nothing verified the chain, and maintaining it serialized every write of a session. A session’s log is ordered by creation time: rows with a session_sequence come first, in that order, then newer rows in the order the API created them.

A refused request is a row with outcome: denied. metadata.auth records how the caller authenticated: the token id, never the secret. A Git clone is the action git.clone. A push is git.push, and metadata.refs lists each ref it created, updated, or deleted.

Retention

Age of an event Where it lives How you read it
0 to 90 days PostgreSQL, one partition per week The audit log in the web app, GET /v1/accounts/:accountId/audit, beeuni audit ls, audit webhooks, and export
90 to 365 days An S3 archive with Object Lock (compliance mode) Export only: GET /v1/accounts/:accountId/audit/export or beeuni audit export
More than 365 days Deleted Not available

Weeks run Monday 00 UTC to Monday 00 UTC. A week moves to the archive once its last instant is 90 days old, so an event becomes export-only between 90 and 97 days after it happened. The archive keeps an event until 365 days after the end of its week, so an event is kept 365 to 372 days in total, and the retention lock means nobody, including Beeuni staff, can shorten or delete it before then. The privacy policy states the same limit: application logs are kept up to 365 days.

Export returns the archive and PostgreSQL as one list, oldest first, with the same filters, the same fields, and the same X-Audit-Next-Cursor paging. Both read paths need the auditAccess entitlement. Archived events carry the same fields as live ones; session_sequence and the integrity hashes are empty on events written since 2026-10 (see above).

Actions

Every row has an action named domain.resource.verb, and a title the web app and beeuni audit ls show:

Action Title Written by
gateway.key.revoke Revoked LLM gateway key A request to DELETE /v1/projects/:projectId/gateway/keys/:keyId.
iam.group.create Created group The request that created the group. The row carries the new group in after.
secret.consumer.used Used secret A sandbox that read a secret.
api.rate_limit.exceeded Hit API rate limit A limiter that refused a request. metadata.limiter names it.

Each API route has exactly one action. A request’s row keeps the route template in metadata.http, and never the raw path, which can carry a token. When a handler records its own event for the request, with the before and after state, that event is the request’s only row. A request that matched no route is api.route.unmatched.

Filter by an action prefix: ?action=iam. on GET /v1/accounts/:accountId/audit, beeuni audit ls --action iam., or an audit webhook’s --action-prefix. Audit log actions lists every action and its title. Rows written before 2026-09-24 carry METHOD /template as their action and show the same titles.

Beeuni background jobs act without a request: they expire grants and tunnel permissions, delete expired session branches, activate App deployments, and move projects between sandbox providers. Their rows have source: worker, and metadata.auth.worker names the job, for example tunnel-cleanup or app-deployments. An App deployment’s row also names the member who started the deployment as its initiator.

An anonymous request that names one of your projects is written to your log. Select these rows with actor_type=anonymous. Anonymous rows are rate-limited on each API process, so a flood of them cannot displace attributed rows. Health probes, CORS preflights, and anonymous traffic to a deployed App are not recorded.

Billing

Free includes one project and $20 in welcome credits, valid for 30 days. From the next period, you receive $2 each month with no rollover. Credits cover computer runtime and official models.

The welcome offer is granted once per personal account. Existing recipients of the old $2 welcome offer receive the $18 difference; accounts that never received it get $20. Creating another team or project does not grant it again. Purchased credits are stored separately and are not cleared by free-credit expiry or monthly resets.

Billing applies at the account level and covers every project in the account. Beeuni offers a free tier, a pro tier, and a per-seat team tier, each with its own model access and credits. Using your own model key does not make usage free: Beeuni still charges a platform fee on top of it, except on the free tier.

Was this page helpful?