Organizations

Organizations

Organizations let multiple users share lists, posting permissions, and (when connected) a shared LinkedIn credential for company-page posting. Roles are owner, admin, and member.

Endpoint table

MethodPathAuthDescription
GET/api/organizationsSessionList organizations. Query: ?public=true for all public orgs, ?userId=... to filter by member.
POST/api/organizationsSessionCreate an organization. Body: name, description, isPublic.
GET/api/organizations/:idSessionGet an organization.
PUT/api/organizations/:idSessionUpdate an organization.
DELETE/api/organizations/:idSessionDelete an organization.
GET/api/organizations/:id/membersSessionList members and their roles.
POST/api/organizations/:id/membersSessionAdd a member. Body: { "userId": "...", "role": "member" }.
PUT/api/organizations/:id/members/:userIdSessionUpdate a member's role (and optionally active).
DELETE/api/organizations/:id/members/:userIdSessionRemove a member.
GET/api/organizations/:id/usersSessionUsers in the organization with role details.
GET/api/organizations/:id/linkedin/statusSessionLinkedIn credential status for the org.
DELETE/api/organizations/:id/linkedin/credentialSessionDisconnect the shared LinkedIn credential.
GET/api/organizations/:id/linkedin/sync-pagesSessionList the discovered LinkedIn company pages.
POST/api/organizations/:id/linkedin/sync-pagesSessionRe-discover and sync LinkedIn company pages.
GET/api/organizations/:id/linkedin/assignmentsSessionPage assignments per member.
PUT/api/organizations/:id/linkedin/assignmentsSessionUpdate page assignments.

Creating an organization

POST /api/organizations
Content-Type: application/json

{ "name": "Acme Dev Team", "description": "Internal engineering org.", "isPublic": false }

Response (201):

{
  "id": "org_001",
  "name": "Acme Dev Team",
  "description": "Internal engineering org.",
  "isPublic": false,
  "createdAt": "2025-06-11T10:00:00.000Z"
}

Members

POST /api/organizations/org_001/members
Content-Type: application/json

{ "userId": "clx9user00003", "role": "member" }

Roles: owner, admin, member. You must be an owner or admin to add or change roles. The last owner cannot be demoted or removed — the server returns 400.

PUT /api/organizations/org_001/members/clx9user00003
Content-Type: application/json

{ "role": "admin" }

Response (200):

{
  "message": "Member role updated successfully",
  "membership": {
    "id": "mem_xyz001",
    "userId": "clx9user00003",
    "organizationId": "org_001",
    "role": "admin",
    "active": true,
    "createdAt": "2025-06-11T10:00:00.000Z"
  }
}

Pass "active": false alongside role to suspend a member's access without removing them from the organization.

LinkedIn organization integration

When an organization's owners or admins connect a shared LinkedIn credential, member-authored messages can be cross-posted as company posts.

Start the OAuth flow:

GET /api/auth/linkedin/org-authorize?organizationId=org_001

This redirects the browser to LinkedIn requesting the rw_organization_admin scope. On completion, the discovered LinkedIn company pages are stored as OrgLinkedInPage records bound to the organization.

MethodPathPurpose
GET /api/organizations/:id/linkedin/statusReturns { "connected": true, "expiresAt": "..." } plus the discovered pages.
POST /api/organizations/:id/linkedin/sync-pagesRe-discover and upsert company pages.
GET /api/organizations/:id/linkedin/assignmentsReturns which member is assigned to post to which page.
PUT /api/organizations/:id/linkedin/assignmentsReplace the assignment map atomically.
DELETE /api/organizations/:id/linkedin/credentialRevoke the shared credential (assignments are cleared server-side).

When a member with an active assignment cross-posts with crossPostToLinkedIn: true (and no explicit linkedInTargets), the post is published using the org credential as the assigned company page. Without an assignment (or after disconnect) cross-posts fall back to the member's personal LinkedIn identity.

For personal LinkedIn pages (not org-bound), see LinkedIn Integration.