Skip to main content

COLLECTIVE API

Programmatic management of collectives — membership, settings, tokens, and published extensions. Every endpoint lives under /api/v1/collectives.

A machine-readable description of this whole surface is served at /api/v1/openapi.json (OpenAPI 3.1, public, no credential required). It covers every endpoint on this page, and a test fails the build if the two ever disagree in either direction.

Authentication

Both credential types from API key authentication work, but they are not interchangeable.

Credential Header Acts as Satisfies requirements by
Personal key (swamp_…) Authorization: Bearer swamp_… The operative who owns it Their role in the collective
Collective token (swamp_org_…) Authorization: Bearer swamp_org_… The collective itself The scopes it holds

Every endpoint requires a credential. Anonymous requests are 401.

Endpoints

Reads

Method Endpoint Scope Role
GET /api/v1/collectives collective:read any
GET /api/v1/collectives/{slug} collective:read any
GET /api/v1/collectives/{slug}/members collective:read any
GET /api/v1/collectives/{slug}/invitations collective:write admin
GET /api/v1/collectives/{slug}/extensions collective:read any
GET /api/v1/collectives/{slug}/serve-instances serve:* member

/invitations is gated one rung higher on both credential types because its response carries email addresses, which appear on no public page. A collective:read token is deliberately not enough — read is the scope you grant for a low-trust credential, and it must not out-rank a member's session on the same data.

Writes

Method Endpoint Scope Role
PATCH /api/v1/collectives/{slug} collective:write admin
POST /api/v1/collectives/{slug}/invite session only admin
DELETE /api/v1/collectives/{slug}/members/{memberId} session only admin
PATCH /api/v1/collectives/{slug}/members/{memberId} session only admin
POST /api/v1/collectives/{slug}/cancel-invite session only admin
POST /api/v1/collectives/create session only
POST /api/v1/collectives/{slug}/delete session only owner

What this API does not do

Two capabilities are absent by decision rather than by oversight, and both are stated in the OpenAPI document as well.

Membership writes accept a session only. Invite, remove, and role changes authorize through the underlying membership guards, which resolve the acting user from a session cookie. A bearer credential — personal key or collective token — presents no cookie, so it cannot drive them. Everything else on this page works with either credential.

There is no audit or activity-log endpoint. No audit store exists behind collectives to serve one from, so rather than return something partial the endpoint is not offered.

Updating settings

PATCH /api/v1/collectives/{slug} takes any subset of four fields. Omitting a field leaves it alone.

curl -X PATCH https://swamp-club.com/api/v1/collectives/acme \
  -H "Authorization: Bearer $SWAMP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description": "We make things", "private": true}'
Field Type Notes
description string or null null clears it. Omitting it leaves it unchanged.
private boolean Hides the collective from non-members.
hideIssuesByDefault boolean New issues from members are hidden by default.
hideMembersFromPublic boolean Stored always; takes effect on the enterprise plan only.

An empty body is 400 rather than a silent success. A personal collective is 400 — personal collectives cannot be updated.

The interactive privacy setting has a fifth option, applyToExisting, which retroactively hides members' existing issues. It is not accepted here: it is a one-way bulk rewrite, and an irreversible bulk action does not belong on an endpoint built to be scripted. Use the collective's settings page for that.

Status codes

Code Meaning
200 Success.
400 Malformed body, a field of the wrong type, or a rule refused the change.
401 No credential.
403 Token lacks the scope or targets another collective; or the role is too low.
404 No such collective — or it is private and you are not a member.

A private collective is deliberately indistinguishable from one that does not exist. Both answer 404, so the status code never confirms a slug to someone guessing.