Connected ID
A ConnectedId is an identity badge: who a person is inside one organization. Org-scoped actions, permissions, and audit attribution all attach to it.
Everyone who exists inside your org carries one — an employee, a service account, a team, and every end user of your applications. It is not a “members only” table.
Two independent axes
Section titled “Two independent axes”Do not mix these up. Almost every bug in this area comes from asking one axis with the other.
| Axis | Question | Field |
|---|---|---|
| Subject | Whose badge is this? | UserId (internal person) · EndUserId (your app’s customer) · both null + Type = Group (team) |
| Grade | What is this person’s standing as a member? | MembershipType — Owner, Boardmember, Admin, Member, Guest, ServiceAccount, None |
Grade only means something for members. A customer’s badge carries MembershipType.None.
Guest is a different thing entirely: an external person you invited (the ORG_GUEST role) —
a contractor, an auditor. They are members.
To ask “is this person a member of the org”, ask the subject axis (UserId != null).
Asking Type != Group instead means “any human badge”, which includes your customers.
Which layer scopes what
Section titled “Which layer scopes what”A badge is per organization, not per app.
| Layer | Scope | Key |
|---|---|---|
EndUser | one organization | OrganizationId + email |
ConnectedId (badge) | one organization | issued once per person per org |
AppAccess | one app group | AppGroupId × EndUserId |
So the same human can be a customer of Org A and of Org B — two separate end users, two separate
badges. And inside Org B they can have access to app group A but not app group B — that split lives
in AppAccess, never in the badge.
Employees who are also customers
Section titled “Employees who are also customers”If someone signing in to your app already holds a member badge in that org (same email), Axowl links the end user to that existing badge instead of issuing a second one. One person, one badge per org — no duplicate identity, no double billing.
That is what is_employee and the cid claim report:
is_employee = false → the badge's subject is an end user (customer only)is_employee = true → the badge's subject is an internal User (also an employee)AxowlPrincipal.ConnectedId is the badge id, and it is set for every end user. It is not the
end user’s id — that is EndUserId.
MembershipType is derived, not set directly
Section titled “MembershipType is derived, not set directly”MembershipType is a projection of the member’s highest RBAC role level, not an independent field:
Role.LevelOwner →Owner; Admin →Admin; Write/Read →Member; no roles →Member.- It is recomputed by
MembershipTierService.RecomputeForMemberAsyncwhenever roles change (MembershipTierService.cs:24). - Excluded from derivation (set explicitly):
ServiceAccount,Guest,Group,Boardmember. Noneis not derived either — it is what a customer badge carries, because grade is a membership concept and a customer is not a member.
Relationships
Section titled “Relationships”ConnectedIds participate in org structure via an edge table — member_of, reporting, and reference relationships (teams, reporting lines). Reporting hierarchy is many-to-many and nestable.