Organization

Manage the workspaces inside your organization from one API key, allocate sender seats, and act on any of them with a single request header.

An organization workspace on Swarmhit holds the billing and the sender-seat pool, and contains any number of ordinary workspaces underneath it. The API mirrors that: one organization key manages every workspace inside it, without a separate key per workspace.

A workspace on a paid plan becomes an organization on the Organization page in Swarmhit (an owner or admin turns it on). An organization key is an API key created in that workspace.

The model

OrganizationWorkspace inside it
Billing and planOwns them (isOrganization: true)Inherits from the parent organization (isSubWorkspace: true)
Sender seatsHolds the pool; seatLimit is the pool minus seats handed outseatLimit is its seatAllocation from the pool
MembersIts own members; its owners and admins also get access to every workspace inside itIts own members, plus read-only organization managers granted via the organization

A seat is one LinkedIn sender account. Mailboxes take no seat: a workspace inside an organization connects as many as it needs on the organization's paid plan, and they are counted apart from seats (mailboxesUsed). See Mailboxes.

The getApiInfo endpoint (GET /) returns the connected workspace with isOrganization, isSubWorkspace, the effective plan, seatLimit and seatsUsed, which makes it a quick way to check what a key (or a header, see below) resolves to.

Managing workspaces

The Organization endpoints live under /workspaces and require the key's workspace to be an organization (anything else gets 403, and listWorkspaces returns isOrganization: false with an empty workspaces list).

Create a workspace and grant it two sender seats from the pool:

curl -X POST https://app.swarmhit.com/api/v1/workspaces \
  -H "Authorization: Bearer swh_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Inc.", "seatAllocation": 2}'

The response is the new workspace, as getWorkspace returns it, including the id to use in the X-Swarmhit-Workspace header.

Seat math is enforced on write. createWorkspace and updateWorkspace reject with 400 an allocation larger than the seats left to assign: the pool, minus the seats the organization uses itself, minus what the other workspaces are allocated (listWorkspaces reports the unallocated remainder as available). updateWorkspace also rejects with 400 an allocation lower than the LinkedIn senders the workspace has connected. seatAllocation is a whole number, 0 or more.

Organizations whose seats are added as needed (the Auto-add seats option in Billing, or a plan that adds seats as senders connect) are not capped by the pool: raising an allocation beyond it first adds the missing seats to the subscription, the same way connecting a sender beyond the pool does. That fails with 400 only when the seats cannot be added, for example when the card is declined. Lowering an allocation never changes the subscription.

deleteWorkspace rejects with 400 while the workspace still has sender accounts or mailboxes: disconnect them first (see Sender accounts and Mailboxes; a bought mailbox is cancelled rather than removed, and counts until its cancellation takes effect).

Workspace members

Each workspace has three membership groups, returned by listWorkspaceMembers and on getWorkspace:

  • members: the workspace's own staff, with roles owner, admin or member.
  • orgMembers: organization owners/admins granted access via the organization. Read-only here; they cannot be changed or removed through the members endpoints.
  • invites: pending email invites (identifier is the invited email, role is admin or member; the invite token is never returned).

Invite someone into a workspace with inviteWorkspaceMember (role defaults to member). Swarmhit emails them the invitation. An invalid email, an address that is already a member, or one with a pending invite is rejected with 400.

curl -X POST https://app.swarmhit.com/api/v1/workspaces/aBc123/members \
  -H "Authorization: Bearer swh_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane@acme.com", "role": "member"}'

updateWorkspaceMember and removeWorkspaceMember work on both members and pending invites: pass type: "user" (default, memberId is the user id) or type: "invite" (memberId is the invite email; on delete, use the ?type=invite query parameter). role is admin or member; any other value sets member. The workspace's owner (ownerId: the user who created it, which through the API is the user who created the key) and the orgMembers cannot have their role changed or be removed (400).

Acting on a workspace: the X-Swarmhit-Workspace header

An organization key can operate on any workspace inside it by naming the target in a request header:

X-Swarmhit-Workspace: <workspaceId>

With the header set, every data endpoint (campaigns, leads, accounts, inbox, and so on) acts on that workspace. Omit it to act on the organization itself. Same endpoint, same key, two workspaces:

# Campaigns of the organization workspace
curl https://app.swarmhit.com/api/v1/campaigns \
  -H "Authorization: Bearer swh_live_xxxxxxxxxxxxxxxxxxxxxxxx"

# Campaigns of workspace aBc123, same key
curl https://app.swarmhit.com/api/v1/campaigns \
  -H "Authorization: Bearer swh_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Swarmhit-Workspace: aBc123"

The rules are strict, and worth internalizing:

  • The header is only honored for an organization key targeting one of its own workspaces. Any other value (a foreign workspace id, a typo, a non-organization key) is rejected with 401, the same response as an invalid key. It never silently falls back to the key's own workspace, so a bad id cannot accidentally read or write the organization's data. An empty value, or the organization's own id, acts on the organization as if the header were absent.
  • The /workspaces endpoints always manage the key's organization, regardless of the header. You cannot nest: a workspace inside an organization cannot contain workspaces of its own.

To confirm what a header resolves to before doing real work, call GET / with the header set. The returned workspace will show the target's id with isSubWorkspace: true.

Everything workspace-scoped follows the header. For example, connectSender with the header set connects the LinkedIn account into that workspace and consumes a seat from its allocation. Hosted connect links follow it too: the sender or mailbox lands in the named workspace, while the page wears the organization's branding and, when a link has no redirectUrl, returns the person to the organization's default return URL. A workspace inside an organization has no billing of its own, so seats added on demand and mailboxes bought for it are charged to the organization. See Authentication for how keys are created and scoped in the first place.

Webhooks per workspace

Webhook endpoints belong to a workspace, and events deliver to the endpoints of the workspace where they occurred. A reply received in one workspace's inbox fires only that workspace's webhooks, never the organization's.

So to receive a workspace's events, register an endpoint on that workspace by calling createWebhook with the X-Swarmhit-Workspace header:

curl -X POST https://app.swarmhit.com/api/v1/webhooks \
  -H "Authorization: Bearer swh_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Swarmhit-Workspace: aBc123" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/acme", "events": ["message.received", "connection.accepted"]}'

You can point every workspace at the same receiver URL. Every payload carries the workspaceId it belongs to, and each endpoint gets its own signing secret at creation, so pick the secret by workspaceId (or by a path or query parameter you bake into each URL) before verifying the signature. Payload verification and retry behavior are covered in the Webhooks guide.

MCP server per workspace

The MCP server does not use the header. The acting workspace is fixed per connection instead: append workspaceId to the connection URL.

https://app.swarmhit.com/mcp?key=swh_mcp_xxxxxxxxxxxxxxxxxxxxxxxx&workspaceId=aBc123

The same rule applies as on REST: the key must be the organization's MCP key and workspaceId must name one of its own workspaces, otherwise every request is rejected with 401. Configure one MCP connection per workspace you want an AI agent to operate.

The organization endpoints have MCP equivalents too: list_workspaces, get_workspace, create_workspace, update_workspace, delete_workspace, invite_workspace_member, update_workspace_member and remove_workspace_member. The ones that act on a single workspace take a workspaceId argument, and all of them manage the key's organization whatever workspace the connection acts on. get_workspace_info is different: it returns the workspace the connection itself resolves to.

A typical onboarding flow

  1. createWorkspace with a name and a seatAllocation from the pool.
  2. inviteWorkspaceMember to give that team access to their workspace.
  3. With X-Swarmhit-Workspace set to the new workspace's id: connect senders, create webhooks, then build campaigns and import leads.
  4. Periodically call listWorkspaces to watch pool / allocated / available and each workspace's sendersUsed against its seatAllocation.

On this page