Summary
The client can create and read organizations and manage members' roles, but cannot delete an org, leave one, suspend a member, or see My Organizations as its own surface. The whole organization-LinkedIn feature — a real, documented product capability — is absent.
Route gaps (all live, Bearer-reachable)
| Route |
What it unlocks |
DELETE /api/organizations/{id} |
Delete an organization (owner only) |
GET /api/user/organizations |
My Organizations — orgs you belong to, with your role |
POST /api/user/organizations |
(probe: likely the join path — confirm before use) |
GET /api/organizations/{id}/linkedin/status |
Org LinkedIn credential status |
POST /api/organizations/{id}/linkedin/sync-pages |
Pull the org's LinkedIn company pages |
PUT /api/organizations/{id}/linkedin/assignments |
Assign company pages to members |
DELETE /api/organizations/{id}/linkedin/credential |
Disconnect the org credential |
Live shape for GET /api/user/organizations (2026-09-07) — richer than the browse list, carrying the caller's role and membership:
{"organizations":[{"id":"fb29220f-…","name":"Bikey Life","slug":"bikey-life",
"description":"…","avatar":null,"isPublic":true,"isSystem":false,
"settings":null,"createdAt":"…","updatedAt":"…","deletedAt":null,
"role":"member","joinedAt":"…","userRole":"member","memberCount":3}]}
Note isSystem — that is how "The Public" is flagged, and it must not offer a Leave action.
Behaviour gaps (/help/organizations)
- Suspend a member. "Change an existing member's role, suspend their access, or remove them using the controls in the members table." macOS
OrgMembersViewModel has changeRole, addMember, removeMember, lookupUser, addMemberByHandle — no suspend.
- Leave an organization. "Use the Leave button next to an organization in the Organizations list or in your My Organizations view."
- Last-owner protection. "The last remaining owner cannot be demoted or removed" and cannot leave until another owner exists. Enforce client-side so the user gets an immediate, specific message rather than a server rejection.
- "The Public" is a system org everyone auto-joins and nobody can leave.
- Creating an org is subscriber-gated (server returns 403 otherwise); joining is free. Covered by the entitlement issue — consume the gate here, do not re-implement it.
Org LinkedIn, in the web's words
An owner or admin connects the org's LinkedIn credential (requesting LinkedIn's organization-admin scope). The company pages LinkedIn returns are stored on the organization. Owners and admins assign specific company pages to specific members. When an assigned member cross-posts to LinkedIn, it can publish to their assigned company page using the shared org credential. Members without an assignment fall back to their personal LinkedIn identity.
For a member who has an assignment, the assigned org page also becomes their default LinkedIn destination … If an owner or admin later disconnects the org's credential, every assigned member falls back to their personal LinkedIn.
That last paragraph is the part with teeth: an org assignment changes where a member's ordinary LinkedIn cross-post goes. The composer's "Posting as …" readiness line (shipped as G11a) must reflect the assigned page, or a member will believe they are posting to their own profile while publishing to a company page.
⚠️ Re-confirm orgScopesEnabled on this tenant before building — the org LinkedIn routes were previously recorded as not deployed and only appeared in the 2026-09-05 spec.
Division of labor
Kit
Domain
App
Tests
Acceptance criteria
- Every organization action the web offers is available on the Mac, with last-owner and system-org rules enforced before the network call.
- A member with an org page assignment can tell where their LinkedIn cross-post is going.
- Full E2E gate green.
Notes
work-consolidation.md tracks this as G25 (formerly G11b, no longer upstream-blocked). Size M. The personal-scope LinkedIn work (PUT /api/linkedin/posting-targets, POST /api/linkedin/sync-pages) lives in the Integrations issue — coordinate so the LinkedIn domain model is written once.
Summary
The client can create and read organizations and manage members' roles, but cannot delete an org, leave one, suspend a member, or see My Organizations as its own surface. The whole organization-LinkedIn feature — a real, documented product capability — is absent.
Route gaps (all live, Bearer-reachable)
DELETE /api/organizations/{id}GET /api/user/organizationsPOST /api/user/organizationsGET /api/organizations/{id}/linkedin/statusPOST /api/organizations/{id}/linkedin/sync-pagesPUT /api/organizations/{id}/linkedin/assignmentsDELETE /api/organizations/{id}/linkedin/credentialLive shape for
GET /api/user/organizations(2026-09-07) — richer than the browse list, carrying the caller's role and membership:{"organizations":[{"id":"fb29220f-…","name":"Bikey Life","slug":"bikey-life", "description":"…","avatar":null,"isPublic":true,"isSystem":false, "settings":null,"createdAt":"…","updatedAt":"…","deletedAt":null, "role":"member","joinedAt":"…","userRole":"member","memberCount":3}]}Note
isSystem— that is how "The Public" is flagged, and it must not offer a Leave action.Behaviour gaps (
/help/organizations)OrgMembersViewModelhaschangeRole,addMember,removeMember,lookupUser,addMemberByHandle— no suspend.Org LinkedIn, in the web's words
That last paragraph is the part with teeth: an org assignment changes where a member's ordinary LinkedIn cross-post goes. The composer's "Posting as …" readiness line (shipped as G11a) must reflect the assigned page, or a member will believe they are posting to their own profile while publishing to a company page.
orgScopesEnabledon this tenant before building — the org LinkedIn routes were previously recorded as not deployed and only appeared in the 2026-09-05 spec.Division of labor
Kit
Organizations.delete(id:),User.organizations(), and the four org-LinkedIn routes + DTOsPOST /api/user/organizationsdoes before building against itDomain
OrganizationsService: delete, leave, suspend-member, my-organizationsisSystemno-leave rule as client-side preconditions with their own error casesApp
Tests
Acceptance criteria
Notes
work-consolidation.mdtracks this as G25 (formerly G11b, no longer upstream-blocked). Size M. The personal-scope LinkedIn work (PUT /api/linkedin/posting-targets,POST /api/linkedin/sync-pages) lives in the Integrations issue — coordinate so the LinkedIn domain model is written once.