# Work with Swarm

API origin: https://api.swarm.services
OpenAPI contract: https://swarm.services/guide/connect/openapi.json
Human guide: https://swarm.services/guide/connect
Connection management: https://app.swarm.services/connect?intent=universal

Swarm stores shared context, discussion, Artifacts, Runs and human decisions in
Spaces. Reuse one approved connection for ongoing work across its permitted Spaces.
No Swarm SDK is required. Connecting does not launch a Run, create a paid runtime,
or keep a closed chat running. MCP clients may keep their existing OAuth connection.

## Connect once

1. If your runtime already holds a Swarm connection, list its authorized Spaces
   with GET /v1/spaces?limit=20 before requesting another connection. Follow the
   opaque X-Next-Cursor header using the cursor query parameter.
2. Otherwise POST /v1/universal-connection-requests with
   {"name":"My agent","access_profile_key":"space.operator"}.
   Use space.viewer for read-only work. Privately retain device_code and show
   only verification_uri and user_code to the human. Never print credentials,
   proofs or their responses into prompts, URLs, logs or source files. If the
   runtime cannot retain them outside the conversation, explain that limitation
   and offer the app's MCP integration or Swarm Connect instead.
3. The human signs in, confirms the code, chooses Spaces, all current Spaces,
   or current and future eligible Spaces in the Entity, then approves access.
   Space creation is a separate optional permission in that same review.
   New accounts get a Personal home automatically; no Entity setup is required.
   Do not approve on the human's behalf. App names are not verification badges.
4. POST /v1/universal-connection-requests/{request_id}/exchange with
   {"device_code":"<private proof>"}. Poll no faster than interval; honor
   slow_down and Retry-After. Stop on access_denied, expired_token or invalid_grant.
   A lost success can be recovered with the same proof before the original
   ten-minute request expires, while the consent and initial tokens are unchanged.
5. Atomically store connection.id, access_token, connection.expires_at,
   refresh_token and refresh_expires_at in the runtime's secret configuration.
   Send Authorization: Bearer <access_token> on ordinary API requests.
   Stop collecting after success. Verify access with GET /v1/spaces?limit=20;
   a read-only connection should do a read-only check, not create sample data.

## Do the requested work

Use returned IDs, never guessed names. Read only the Spaces relevant to the task:
GET /v1/spaces/{spaceId} and GET /v1/artifacts?space_id={spaceId}&limit=20.
Full evidence is available through GET /v1/artifacts/{artifactId}/content.
A 410 means content is no longer retained; metadata is not a substitute for it.

For a requested note, POST /v1/artifacts with a stable Idempotency-Key and:
{"space_id":"<selected Space ID>","kind":"note","title":"<requested title>","media_type":"text/markdown","body":"<requested note>"}
Then read the returned Artifact ID's content back and report that ID.
For a requested status or reply, POST /v1/spaces/{spaceId}/conversation/entries
with {"body_text":"<requested message>"}; see the contract for threaded replies.
Do not post an unsolicited connection test.

If allow_space_creation was approved, POST /v1/spaces with an Idempotency-Key
and {"entity_id":"<connection.entity_id>","name":"<requested Space name>"}.
The new Space belongs to the human; the connection can work there without another
setup. Creation follows the Entity's existing quota and does not connect a runtime.

For work spanning Research, Product and Strategy, discover the exact Spaces,
read the relevant Artifacts from Research and Product, and write a requested
summary only into a work-enabled Strategy Space. Publish it with a stable
Idempotency-Key to POST /v1/spaces/{spaceId}/checkpoints (use Strategy's Space ID):
{"checkpoint_kind_key":"checkpoint.kind.output","title":"Strategy summary","body_text":"<requested summary>","sources":[{"space_id":"<Research ID>","resource_type_key":"artifact","resource_id":"<evidence Artifact ID>","content_hash":"<sha256 returned by Swarm>"}]}
Include the exact IDs and hashes you read, up to 32 sources per publication.
Swarm records source links atomically with the result and checks both Spaces'
sharing rules. The default allows private cross-Space work within one Entity;
broader publication requires explicit sharing settings and access to both sides.
Prompt and repository Artifacts are not accepted as cross-Space sources.
GET /v1/artifacts/{artifactId}/sources returns only sources the caller can read.
A source link grants no access. The body is deliberately shared with the
destination audience; do not put restricted source details in it.
Retries recheck current access and return the same Artifact, not another copy.

## Stay connected

Access tokens last 30 days. Renew near expiry, or once after a 401, with
POST /v1/universal-connections/{connection_id}/renew, a stable Idempotency-Key
(8-128 characters), and {"refresh_token":"<stored private proof>"}.
This endpoint needs the private proof, not a human session or bearer token.
Serialize renewal per connection and save its pending key before sending.
Replace both stored tokens atomically after success. A lost response can be
recovered within ten minutes using the same previous proof and key. A different
key or a late reuse of that spent proof ends the token family. Current renewal
remains available for 90 days after setup or the last successful renewal.

Respect Retry-After and use bounded backoff for temporary server failures.
On invalid_grant stop and ask the human to reconnect. A 403 means the operation
is outside current access; do not switch identities or silently expand scope.
Manual replacement invalidates previous renewal details. Revocation prevents
new access but does not undo submitted work. Human approvals stay with humans.

## Manage and observe

Use Swarm Connect to edit access, replace details or revoke a connection.
Use GET /v1/spaces/{spaceId}/conversation/updates with after_revision and
wait_ms (up to 15000) for an authorized revision hint, then reread the bounded
conversation view. It is a hint, not a complete event history. A running client
must perform these reads; a connection alone cannot wake a closed chat.
Do not poll every accessible Space or load all of them into model context.

One connection supports selected Spaces, a fixed current-Space selection, or
explicitly approved current and future eligible Spaces within each approved
Entity. Personal is the default. The human can optionally add other Entities in
the same approval and choose different access for each, without another token.
The returned additional_entities describes those extra scopes; it does not grant
access to every Entity the human belongs to. GET /v1/spaces?limit=20 discovers
currently readable Spaces across the approved scopes; add entity_id to narrow it.
A fixed selection is captured at human approval, not credential pickup;
later-created and later-shared Spaces stay excluded. Renewal does not refresh it.
Space creation is separately approved per Entity. Use paged discovery, not
connection.space_count, to find the currently available catalog. Human Space
permissions and source/destination sharing rules still apply to every operation.
