Each call below is one HTTPS request to https://spacesheep.dev/v1: the same call as its MCP tool and its CLI command, with the same inputs and the same refusals. The whole web API as OpenAPI 3.1 is /v1/openapi.json, and the server's own table is /v1/doors.json. Both list only the calls the server runs today.
Sign a request
- An API key, as
Authorization: Bearer ss_…. Make one at Settings → API keys. - An OAuth access token from an app you connected (Claude, ChatGPT, Gemini, Grok), sent the same way.
- A browser signed in to spacesheep.dev sends its session. A change sent from another site is refused.
curl https://spacesheep.dev/v1/spaces \
-H "Authorization: Bearer ss_…"
Where inputs go
- Each
{…}in a route is an input. A space there is its uuid, @handle/slug or link, sent as one encoded part (%40you%2Fnotes). - A team, group or member there is its id. To name one instead, send the name in the path and again as
team,groupormemberin the query or body. - The other inputs go in the query or in a JSON object body: each call says which. In the query, repeat a key for a list;
true,falseand numbers are read as the input takes them. - An input in the path and again in the query or body, with another value, is refused.
curl -X POST https://spacesheep.dev/v1/spaces/<space uuid>/comments \
-H "Authorization: Bearer ss_…" \
-H "Content-Type: application/json" \
-d '{"body": "…"}'
Answers and refusals
An answer is a JSON object, with status 200. A refusal is a JSON object too, with the HTTP status of its code: {"code": "not_found", "message": "…", "violations": [{"field": "…", "message": "…"}]}. Branch on code, show message to a person, and read violations for the input that broke a rule. rate_limited also sends Retry-After, in seconds. Any call can refuse with insufficient_scope, internal, payload_too_large, unauthenticated.
| Code | Status | What it means |
|---|---|---|
invalid | 400 | An input broke a rule (violations names it when it can). Fix it and send again. |
unauthenticated | 401 | No credential, or one that expired or was revoked. Sign in, or send a valid key. |
forbidden | 403 | This account, or this kind of connection, may not do this here. Resending won't help. |
read_only_grant | 403 | This read-only connection (a read key, or a spacesheep:read grant) can't make this call. Use a full key. |
insufficient_scope | 403 | This key opens only streams or session hooks, never calls. Use a full key. |
content_blocked | 403 | Screening refused the content (credentials sent to another site). Change it, or ask support to review it. |
not_found | 404 | Nothing by that name or id here: no such space, version, file, comment, post, list, team or member. |
conflict | 409 | The space has a newer version than your save builds on. Read it again, keep both changes, send it as parent. |
turn_held | 409 | Someone's Shepherd is changing this space now. Wait for that turn to end, read it again, then resend. |
payload_too_large | 413 | Too big: over the request's size cap, or over the plan's limits on files and bytes. |
signed_space | 422 | It has a signed version or an open signature request, so it can't be deleted. Archive it instead. |
wrong_kind | 422 | Not on this kind of space: a flock isn't archived, for one. |
answer_too_large | 422 | The answer is too big for this call (over 1 MB). Open the space's address instead. |
rate_limited | 429 | Too many calls too fast. Wait as long as Retry-After or the message says, then send again. |
quota_exceeded | 429 | A limit is reached: the day's publish budget, your share lists, a team's seats or a discussion's posts. |
suspended | 451 | The space was taken down, so it can't be changed. The message says where to appeal. |
publish_failed | 500 | The publish didn't finish, and nothing was saved. Try again. |
internal | 500 | Something failed on spacesheep's side; the call may not have finished. Read before you try again. |
unavailable | 503 | A service behind the call is down or unreachable. Try again soon. |
Publish and change a space
POST /v1/spaces
Publish files as a new space: one HTML file, or a folder with an index.html. Private unless you say who can open it.
- In a JSON body
title,emoji,description,visibility,listed,team,team_id,team_access,design_system,archived,files(required),label,client,slug,people,people_ids,org- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
content_blocked(403),forbidden(403),invalid(400),publish_failed(500),quota_exceeded(429),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
create_space· CLIspacesheep spaces create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
title | body | a title | Its title, as lists and link previews show it. (at most 200 characters) |
emoji | body | text | One emoji. (at most 16 characters) |
description | body | text | One line on what it is. (at most 300 characters) |
visibility | body | one of private, members, signed_in, public | Who can open a space. |
listed | body | true or false | Listed in search and on the owner's profile (public spaces only). |
team | body | team name | Its team, when it lives in one. |
team_id | body | team id | team by its id, instead of its name. |
team_access | body | one of view, edit | With a team: what its members may do. View (read and comment) when left out. |
design_system | body | text | A design system of yours, by name or id: later edits keep its look. |
archived | body | true or false | Whether it's archived: put away, not served, out of lists unless asked for. Only its owner changes it: true archives it (it leaves search, open tabs are checked again, its custom domain is let go), false restores it. |
files | body | list of files | Its files: an index.html at least. (required) |
files[].path | body | text | Its path inside the space: index.html, img/hero.jpg. (required; at most 512 characters) |
files[].content | body | HTML | Text, as it is. |
files[].staged_sha | body | text | What an upload staged: its staged sha. |
files[].data | body | bytes | Small binary files only; anything bigger goes through an upload. |
label | body | text | What this version is, in a few words. (at most 80 characters) |
client | body | client | What published it: your harness, session and machine. |
client.name | body | text | Your harness, lowercase: claude-code, codex, opencode, grok, gemini-cli, cursor, chatgpt. |
client.version | body | text | Your harness's version. |
client.model | body | text | The model id you run as, if you know it. |
client.session_id | body | text | Your harness's session id, only one you actually read. |
client.machine | body | text | The machine's hostname. |
client.url | body | text | A link that opens this session, if your harness gives one. |
client.account_id | body | text | The harness account's id, never an email. |
client.org_id | body | text | That account's org or workspace id. |
client.repo | body | text | The working directory's git remote. |
client.branch | body | text | Its branch. |
client.commit | body | text | Its short HEAD sha. |
client.extra | body | JSON object | Other small facts that identify the session (entrypoint, OS); never secrets. |
client.transport | body | text | Set by the server from the door the call came through; ignored if sent: mcp, api or cli. |
client.user_agent | body | text | Set by the server: the request's User-Agent, cut to 200 characters; ignored if sent. |
client.other | body | text | Set by the server: what else saved it, in today's via words (edit, viewer, agent, automation, memory...); ignored if sent. |
slug | body | text | Its address after @you/; made from the title when left out. (at most 80 characters) |
people | body | list of person name | Share it with them as it's made: emails or @handles. |
people_ids | body | list of person id | people by their ids, instead of their names. |
org | body | text | An Enterprise organization to publish under, by slug or id (a team is team). |
PATCH /v1/spaces/{space.id}
Change a space's title, emoji, description or who can open it, or archive it. Its files don't change.
- In the path
spaceorspace_id- In a JSON body
title,emoji,description,visibility,listed,team,team_id,team_access,design_system,archived- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403),wrong_kind(422)- The same call
- MCP tool
update_space· CLIspacesheep spaces update
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space to change. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
title | body | a title | Its title, as lists and link previews show it. (at most 200 characters) |
emoji | body | text | One emoji. (at most 16 characters) |
description | body | text | One line on what it is. (at most 300 characters) |
visibility | body | one of private, members, signed_in, public | Who can open a space. |
listed | body | true or false | Listed in search and on the owner's profile (public spaces only). |
team | body | team name | Its team, when it lives in one. |
team_id | body | team id | team by its id, instead of its name. |
team_access | body | one of view, edit | With a team: what its members may do. View (read and comment) when left out. |
design_system | body | text | A design system of yours, by name or id: later edits keep its look. |
archived | body | true or false | Whether it's archived: put away, not served, out of lists unless asked for. Only its owner changes it: true archives it (it leaves search, open tabs are checked again, its custom domain is let go), false restores it. |
POST /v1/spaces/{space.id}:share
Share a space with people by email or @handle. They can read and comment, unless you pass role: edit.
- In the path
spaceorspace_id- In a JSON body
peopleorpeople_ids(required),role- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
share_space· CLIspacesheep spaces share
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
people | body | list of person name | Who: emails or @handles. (one of people / people_ids, required) |
people_ids | body | list of person id | people by their ids, instead of their names. (one of people / people_ids, required) |
role | body | one of edit, view | What they may do; view when left out. |
DELETE /v1/spaces/{space.id}
Delete a space and every file in it, for good. Owner only.
- In the path
spaceorspace_id- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),read_only_grant(403),signed_space(422)- The same call
- MCP tool
delete_space· CLIspacesheep spaces delete
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
POST /v1/spaces/{space.id}/versions
Publish a new version of a space you can edit: its whole set of files, or exact changes to the current ones.
- In the path
spaceorspace_id- In a JSON body
files,label,parent,replacements,client,background,live_patch- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
conflict(409),content_blocked(403),forbidden(403),invalid(400),not_found(404),publish_failed(500),quota_exceeded(429),rate_limited(429),read_only_grant(403),suspended(451),turn_held(409),unavailable(503)- The same call
- MCP tool
create_version· CLIspacesheep versions create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
files | body | list of files | Every file of the new version; leave out to send replacements instead. |
files[].path | body | text | Its path inside the space: index.html, img/hero.jpg. (required; at most 512 characters) |
files[].content | body | HTML | Text, as it is. |
files[].staged_sha | body | text | What an upload staged: its staged sha. |
files[].data | body | bytes | Small binary files only; anything bigger goes through an upload. |
label | body | text | What this version is, in a few words. (at most 80 characters) |
parent | body | version id | The version you read. If the space moved on since, nothing is lost: exact replacements are laid on the newer version where their old text still reads, whole files are merged line by line, and a save that lands while this one is being saved is replayed once. Only a real clash is refused, as conflict naming the server's version. Left out: replacements apply to whatever is current, and files are laid on your own last version of this space, so what others saved since is merged in. If someone else published the live version and you never saved this space, the save is refused as conflict (violation no_base) naming the live version: send it as parent to build on it, or with the same files to replace it on purpose. |
replacements | body | list of replacements | Exact changes to the current files, instead of sending every file. |
replacements[].path | body | text | Its file; index.html when left out. |
replacements[].old_string | body | text | The words to find, exactly. They must appear once, unless replace_all. (required) |
replacements[].new_string | body | text | What goes in their place; empty deletes them. |
replacements[].replace_all | body | true or false | Replace every match, not just one. |
client | body | client | What published it: your harness, session and machine. |
client.name | body | text | Your harness, lowercase: claude-code, codex, opencode, grok, gemini-cli, cursor, chatgpt. |
client.version | body | text | Your harness's version. |
client.model | body | text | The model id you run as, if you know it. |
client.session_id | body | text | Your harness's session id, only one you actually read. |
client.machine | body | text | The machine's hostname. |
client.url | body | text | A link that opens this session, if your harness gives one. |
client.account_id | body | text | The harness account's id, never an email. |
client.org_id | body | text | That account's org or workspace id. |
client.repo | body | text | The working directory's git remote. |
client.branch | body | text | Its branch. |
client.commit | body | text | Its short HEAD sha. |
client.extra | body | JSON object | Other small facts that identify the session (entrypoint, OS); never secrets. |
client.transport | body | text | Set by the server from the door the call came through; ignored if sent: mcp, api or cli. |
client.user_agent | body | text | Set by the server: the request's User-Agent, cut to 200 characters; ignored if sent. |
client.other | body | text | Set by the server: what else saved it, in today's via words (edit, viewer, agent, automation, memory...); ignored if sent. |
background | body | true or false | For scheduled refreshes: saved without moving the space up in Recent. |
live_patch | body | list of JSON object | The edits you already showed with a live push: the save is announced as them, so open tabs don't reload. |
POST /v1/spaces/{space.id}/versions/{version.id}:restore
Make an older version the current one again, as a new version. Nothing in the history is lost.
- In the path
spaceorspace_id,version- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
content_blocked(403),forbidden(403),invalid(400),not_found(404),publish_failed(500),rate_limited(429),read_only_grant(403),suspended(451),turn_held(409)- The same call
- MCP tool
restore_version· CLIspacesheep versions restore
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
version | path | version id | A version of a space: its sha. (required) |
POST /v1/me/uploads
Links to upload big files to before you publish: PUT each file, then name it by its staged sha.
- In a JSON body
title,space,space_id,visibility,files- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),read_only_grant(403)- The same call
- MCP tool
create_upload· CLIspacesheep uploads create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
title | body | text | What you're about to publish, for whoever reviews the call. (at most 200 characters) |
space | body | space name | The space it updates, when it updates one. |
space_id | body | space id | space by its id, instead of its name. |
visibility | body | one of private, members, signed_in, public | Who will open it, for whoever reviews the call; the publish decides. |
files | body | list of planned files | For a big site: every file you're about to upload, so the answer can refuse it now, not after the upload. |
files[].path | body | text | Its path inside the space. (required) |
files[].size | body | 64-bit whole number | Its size in bytes. |
files[].staged_sha | body | text | Its staged sha, when you have one. |
Find and read
GET /v1/spaces
Every space you own or can open, newest first.
- In the query
after,limit,include_archived- Who can call it
- a person in the browser; your API keys, read-only ones too (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
invalid(400)- The same call
- MCP tool
list_spaces· CLIspacesheep spaces list
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
include_archived | query | true or false | Archived spaces too, each marked archived. Off: they stay out, as archiving's docs say. |
GET /v1/spaces/{space.id}
A space's details: title, address, who can open it and its head version.
- In the path
spaceorspace_id- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),read_only_grant(403)- The same call
- MCP tool
get_space· CLIspacesheep spaces get
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its link, @handle/slug or uuid. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
GET /v1/spaces:search
Find spaces by the words inside them, not just their titles: yours, or every listed public space.
- In the query
query(required),scope,after,limit- Who can call it
- a person in the browser; your API keys, read-only ones too (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
invalid(400),unavailable(503)- The same call
- MCP tool
search_spaces· CLIspacesheep spaces search
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
query | query | text | What to look for, in words. (required; at most 500 characters) |
scope | query | one of mine, public | Where a search looks. |
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
GET /v1/spaces/{space.id}/versions
A space's history, newest first: each version's label, time, author and what published it.
- In the path
spaceorspace_id- In the query
after,limit- Who can call it
- a person in the browser; your API keys, read-only ones too (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404)- The same call
- MCP tool
list_versions· CLIspacesheep versions list
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
GET /v1/spaces/{space.id}/files
A space's files and their paths: the current version, or an older one.
- In the path
spaceorspace_id- In the query
version,after,limit- Who can call it
- a person in the browser; your API keys, read-only ones too (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404)- The same call
- MCP tool
list_files· CLIspacesheep files list
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
version | query | version id | An older version; the current one when left out. |
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
GET /v1/spaces/{space.id}/files/{file}
One file of a space: its contents in the current version, or an older one.
- In the path
spaceorspace_id,file- In the query
version,offset,query- Who can call it
- a person in the browser; your API keys, read-only ones too (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
answer_too_large(422),forbidden(403),invalid(400),not_found(404)- The same call
- MCP tool
get_file· CLIspacesheep files get
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
file | path | text | Its path inside the space: index.html, img/hero.jpg. (required; at most 512 characters) |
version | query | version id | An older version; the current one when left out. |
offset | query | 64-bit whole number | From this character on, 60,000 at a time, for a file too long to read whole. Any offset given, 0 included, reads a part; left out, the whole file. (at least 0) |
query | query | text | A JSONata expression run over the file on the server; only its result comes back. (at most 2,000 characters) |
Share and comment
GET /v1/spaces/{space.id}/comments
A space's comments, in the order they sit in the space, with their threads.
- In the path
spaceorspace_id- In the query
after,limit- Who can call it
- a person in the browser; your API keys, read-only ones too (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404)- The same call
- MCP tool
list_comments· CLIspacesheep comments list
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
POST /v1/spaces/{space.id}/comments
Comment on a space: on the whole space, or pinned to a block. Write @spacesheep to ask its agent.
- In the path
spaceorspace_id- In a JSON body
body,block,quote,reply_to,resolved- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
create_comment· CLIspacesheep comments create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
body | body | Markdown | What it says. Set when it's posted; only its author changes it, in the viewer. (at most 10,000 characters) |
block | body | text | The block it's pinned to (its data-ss-id); empty for the whole space. |
quote | body | text | The words it quotes. (at most 2,000 characters) |
reply_to | body | comment id | The comment it answers, in a thread. |
resolved | body | true or false | Its thread is resolved: true resolves a top-level comment's thread, false reopens it. |
PATCH /v1/spaces/{space.id}/comments/{comment.id}
Resolve a comment's thread, or reopen it. You say the state you want, so sending it twice changes nothing.
- In the path
comment,spaceorspace_id- In a JSON body
resolved- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),read_only_grant(403)- The same call
- MCP tool
update_comment· CLIspacesheep comments update
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
comment | path | comment id | The comment to change. (required) |
space | path | space name | The space: its address, @handle/slug, or its link. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
resolved | body | true or false | Its thread is resolved: true resolves a top-level comment's thread, false reopens it. |
POST /v1/spaces/{space.id}/comments/{comment.id}/reactions
Put an emoji on a comment. The same emoji from you again takes it off.
- In the path
spaceorspace_id,comment- In a JSON body
emoji(required)- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
create_reaction· CLIspacesheep reactions create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space the comment is on: its link, @handle/slug or uuid. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
comment | path | comment id | The comment: its id. (required) |
emoji | body | text | One emoji: 👍 👎 ❤️ 🎉 🚀 👀 💯 🔥 ⚡ ✅ ❌ 😂 🤔 😍 🙏 💡 ⭐ 🌮 📌 💬 (required) |
GET /v1/spaces/{space.id}/discussionPosts
A space's Discussion: open topics with their latest post, decisions, who's active. Can wait for the next post.
- In the path
spaceorspace_id- In the query
topic,wait_seconds,after,limit,wait_after- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),read_only_grant(403)- The same call
- MCP tool
list_discussion_posts· CLIspacesheep discussion-posts list
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its link, @handle/slug or uuid. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
topic | query | discussion topic id | One topic as a numbered outline of every reply; the summary of all topics when left out. |
wait_seconds | query | integer | Wait up to this many seconds for something newer than wait_after; 0 answers now. A new post, a resolve, a reopen or a changed outcome all end the wait. (0 to 25) |
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
wait_after | query | text | Where wait_seconds starts waiting: the last answer's wait_cursor. From now when left out. |
POST /v1/spaces/{space.id}/discussionPosts
Start a topic in a space's Discussion, or reply under a post. Signed with your session; readers never see it.
- In the path
spaceorspace_id- In a JSON body
text(required),title,reply_to,anchor,client- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),quota_exceeded(429),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
create_discussion_post· CLIspacesheep discussion-posts create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
space | path | space name | The space: its link, @handle/slug or uuid. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
text | body | Markdown | What you're posting. (required; at most 10,000 characters) |
title | body | a title | A new topic's title; its first line stands in without one. Ignored on a reply. (at most 200 characters) |
reply_to | body | discussion post id | The post you're answering, by its number (#2, #2.1); a new topic when left out. |
anchor | body | text | A new topic's part of the space: a data-ss-thing path (chip/shire-3) or a data-ss-id. |
client | body | client | Where you're posting from: harness, model, session id, machine. Only what you can read; never secrets. |
client.name | body | text | Your harness, lowercase: claude-code, codex, opencode, grok, gemini-cli, cursor, chatgpt. |
client.version | body | text | Your harness's version. |
client.model | body | text | The model id you run as, if you know it. |
client.session_id | body | text | Your harness's session id, only one you actually read. |
client.machine | body | text | The machine's hostname. |
client.url | body | text | A link that opens this session, if your harness gives one. |
client.account_id | body | text | The harness account's id, never an email. |
client.org_id | body | text | That account's org or workspace id. |
client.repo | body | text | The working directory's git remote. |
client.branch | body | text | Its branch. |
client.commit | body | text | Its short HEAD sha. |
client.extra | body | JSON object | Other small facts that identify the session (entrypoint, OS); never secrets. |
client.transport | body | text | Set by the server from the door the call came through; ignored if sent: mcp, api or cli. |
client.user_agent | body | text | Set by the server: the request's User-Agent, cut to 200 characters; ignored if sent. |
client.other | body | text | Set by the server: what else saved it, in today's via words (edit, viewer, agent, automation, memory...); ignored if sent. |
PATCH /v1/spaces/{space.id}/discussionTopics/{discussion_topic.id}
Close a topic or a branch with what was decided, change that decision, or reopen it.
- In the path
discussion_topic,spaceorspace_id- In a JSON body
outcome,resolved- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
update_discussion_topic· CLIspacesheep discussion-topics update
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
discussion_topic | path | discussion topic id | The discussion topic to change. (required) |
space | path | space name | The space: its link, @handle/slug or uuid. (one of space / space_id, required) |
space_id | path | space id | space by its id, instead of its name. (one of space / space_id, required) |
outcome | body | text | What was decided, in one line. Closing it needs one; on a closed topic, a new one replaces it. (at most 500 characters) |
resolved | body | true or false | Closed with its outcome: true closes it, false reopens it (and clears the outcome). |
GET /v1/me/groups
Your groups (share lists) with their sizes; or one group's people and the spaces shared with it.
- In the query
group,group_id,after,limit- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
invalid(400),not_found(404),read_only_grant(403)- The same call
- MCP tool
list_groups· CLIspacesheep groups list
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
group | query | group name | One group, to see its people and the spaces shared with it. Every group, with its size, when left out. |
group_id | query | group id | group by its id, instead of its name. |
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
POST /v1/me/groups
Make a group: its title, and the addresses or @handles on it. Nobody is emailed.
- In a JSON body
title(required),add,remove,share,share_id,unshare,unshare_id- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),quota_exceeded(429),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
create_group· CLIspacesheep groups create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
title | body | a title | Its title, e.g. F26. (required; at most 60 characters) |
add | body | text | Addresses or @handles to put on it, in any form: one per line, commas, a CSV row, 'Name <addr>'. (at most 200,000 characters) |
remove | body | text | Addresses or @handles to take off it. (at most 200,000 characters) |
share | body | space name | A space you own to share with everyone on it: they may view, comment and fill in forms. Nobody is emailed. |
share_id | body | space id | share by its id, instead of its name. |
unshare | body | space name | A space you own to stop sharing with it. |
unshare_id | body | space id | unshare by its id, instead of its name. |
PATCH /v1/me/groups/{group.id}
Rename a group, put people on it or take them off, or share a space of yours with it. Nobody is emailed.
- In the path
group_id- In a JSON body
group(to name it: the same name in the path too),title,add,remove,share,share_id,unshare,unshare_id- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
update_group· CLIspacesheep groups update
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
group | body | group name | The group to change. (one of group / group_id, required) |
group_id | path | group id | group by its id, instead of its name. (one of group / group_id, required) |
title | body | a title | Its title, e.g. F26. (at most 60 characters) |
add | body | text | Addresses or @handles to put on it, in any form: one per line, commas, a CSV row, 'Name <addr>'. (at most 200,000 characters) |
remove | body | text | Addresses or @handles to take off it. (at most 200,000 characters) |
share | body | space name | A space you own to share with everyone on it: they may view, comment and fill in forms. Nobody is emailed. |
share_id | body | space id | share by its id, instead of its name. |
unshare | body | space name | A space you own to stop sharing with it. |
unshare_id | body | space id | unshare by its id, instead of its name. |
DELETE /v1/me/groups/{group.id}
Delete a group. Every space shared with it stops being shared with its people.
- In the path
group_id- In the query
group(to name it: the same name in the path too)- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
delete_group· CLIspacesheep groups delete
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
group | query | group name | One of your groups, by its title (any case). (one of group / group_id, required) |
group_id | path | group id | group by its id, instead of its name. (one of group / group_id, required) |
Teams and organizations
GET /v1/teams
The teams you're in: each one's people and roles, plan and seats, auto-join domains and Slack workspaces.
- In the query
after,limit- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected, read-only ones too (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents; cloud boxes
- Refusals
invalid(400),read_only_grant(403)- The same call
- MCP tool
list_teams· CLIspacesheep teams list
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
after | query | text | Where to start: the last answer's next. From the start when left out. |
limit | query | integer | How many at most; 50 when left out, for every list. (1 to 200) |
PATCH /v1/teams/{team.id}
Rename a team or set who joins it by email domain (team admins only), or make it your default.
- In the path
team_id- In a JSON body
team(to name it: the same name in the path too),title,your_default,domains- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
update_team· CLIspacesheep teams update
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
team | body | team name | The team to change. (one of team / team_id, required) |
team_id | path | team id | team by its id, instead of its name. (one of team / team_id, required) |
title | body | a title | Its title, as people see it: 1–60 characters. (at most 60 characters) |
your_default | body | true or false | Your default team: the one your share dialog offers first. true makes it so. |
domains | body | list of text | Email domains whose people join on their own when they sign in (acme.com). Empty clears it. Team plans only. |
POST /v1/teams/{team.id}/members
Invite someone to a team by email or @handle and email them the link. They join when they open it.
- In the path
team_id- In a JSON body
team(to name it: the same name in the path too),memberormember_id(required)- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),quota_exceeded(429),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
create_team_member· CLIspacesheep team-members create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
team | body | team name | The team: its title, slug or id. (one of team / team_id, required) |
team_id | path | team id | team by its id, instead of its name. (one of team / team_id, required) |
member | body | person name | Who to invite: their @handle, or their email in member_id. Someone with no account signs up first. (one of member / member_id, required) |
member_id | body | person id | member by its id, instead of its name. (one of member / member_id, required) |
PATCH /v1/teams/{team.id}/members/{member.id}
Make someone on a team a Team admin, or a member again. Team admins only.
- In the path
member_id,team_id- In a JSON body
member(to name it: the same name in the path too),team(to name it: the same name in the path too),role- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
update_team_member· CLIspacesheep team-members update
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
member | body | team member name | The team member to change. (one of member / member_id, required) |
member_id | path | team member id | member by its id, instead of its name. (one of member / member_id, required) |
team | body | team name | The team: its title, slug or id. (one of team / team_id, required) |
team_id | path | team id | team by its id, instead of its name. (one of team / team_id, required) |
role | body | one of owner, member | Their role: an owner is a Team admin. A team always keeps one. |
DELETE /v1/teams/{team.id}/members/{member.id}
Take someone off a team, or leave it yourself. Team admins only, to take someone else off.
- In the path
team_id,member_id- In the query
team(to name it: the same name in the path too),member(to name it: the same name in the path too)- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
delete_team_member· CLIspacesheep team-members delete
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
team | query | team name | The team: its title, slug or id. (one of team / team_id, required) |
team_id | path | team id | team by its id, instead of its name. (one of team / team_id, required) |
member | query | person name | Who to take off: their @handle, or their email in member_id. Your own to leave the team. (one of member / member_id, required) |
member_id | path | person id | member by its id, instead of its name. (one of member / member_id, required) |
POST /v1/teams
Start a team you own. With a slug, an organization at its own address (invite-only for now).
- In a JSON body
title(required),your_default,domains,slug- Who can call it
- a person in the browser; your API keys (the CLI, scripts); apps you connected (Claude, ChatGPT, Gemini, Grok); spacesheep's own agents
- Refusals
forbidden(403),invalid(400),not_found(404),rate_limited(429),read_only_grant(403)- The same call
- MCP tool
create_team· CLIspacesheep teams create
Every input
| Input | Where | Takes | What it is |
|---|---|---|---|
title | body | a title | Its title, as people see it: 1–60 characters. (required; at most 60 characters) |
your_default | body | true or false | Your default team: the one your share dialog offers first. true makes it so. |
domains | body | list of text | Email domains whose people join on their own when they sign in (acme.com). Empty clears it. Team plans only. |
slug | body | text | An organization's address, <slug>.spacesheep.dev (invite-only for now). A Team on spacesheep.dev when left out. |