HTTP API
Every endpoint of the platform API, drawn from the contract.
On this page
- Base URL
- Authentication
- Workspaces
- Requests and replies
- Errors
- Limits
- Deploying through the API
- Endpoints
- Projects, builds and links
- listProjects
- createProject
- getProject
- startUpload
- createBuild
- listBuilds
- makeLive
- createLink
- listLinks
- revokeLink
- revealLink
- reissueLink
- listVisits
- listResponses
- listComments
- pipeline
- addPage
- listWorkspaces
- createTeam
- listMembers
- setMemberRole
- removeMember
- leaveTeam
- renameWorkspace
- listInvitations
- createInvitation
- revokeInvitation
- viewInvitation
- acceptInvitation
- deleteTeam
- projectHistory
- Changing a project
- renameProject
- deleteProject
- getRoomSetup
- orderApps
- orderPages
- removePage
- listFeedback
- resolveComment
- replyToComment
- Pausing a room
- getRoomPause
- pauseRoom
- resumeRoom
- projectHistory
- The tour
- getTour
- Analytics
- buildHistory
- journey
- projectAnalytics
- workspaceAnalytics
- The pipeline
- board
- projectBoard
- placeLink
- judgeLink
- followUp
- listNotes
- addNote
- removeNote
- Feedback
- getFeedback
- Room setup
- getNextSteps
- setNextSteps
- getBrand
- setBrand
- removeLogo
- getTheme
- setTheme
- resetTheme
- checkTheme
- getProfile
- setProfile
- The community
- getCard
- saveCard
- removeCover
- checkBuildCover
- coverFromBuild
- listProject
- rehearseListing
- appealListing
- listNotices
- readNotices
- scanCard
- unlistProject
- suggestWording
- previewPass
- getAccess
- setAccess
- communityPass
- Arranging projects
- getArrangement
- arrangeProjects
- createGroup
- renameGroup
- deleteGroup
- Public
- profile
- listings
- Plan and usage
- getBilling
- projectBounces
- You, and your workspace’s people
- whoami
- setName
- removePhoto
- deletable
- deleteAccount
- containerUsage
- members
- signIns
- disconnect
- sessions
- revokeSession
- signOutEverywhere
- Your first days
- myPreferences
- welcomeSeen
- foldFeedbackTab
- sendProductFeedback
- Reports
- reportProject
The platform API is what the app, pitch and the connector all call: one
contract, checked on both sides, so none of them can do what the API does not
allow. You can call it too, from a script or your own tool. This page says
how to sign in, how requests and replies are shaped, how an upload works, and
lists every endpoint.
Base URL
https://api.getroomi.comEvery endpoint is under /api. GET /healthz answers
{"ok":true,"service":"platform"} without signing in, for a check that the
platform is up.
Authentication
Every endpoint needs you signed in, except sign-in itself under /api/auth/
and the community's public pages. Without a session, an endpoint answers
401 with {"error":"sign in first"}.
There are two ways to be signed in:
- A cookie, which the app uses. Signing in to the app sets it.
- A bearer token, which
pitchuses, sent asAuthorization: Bearer <token>.
To call the API from a script, sign in with pitch login
and read the header with pitch auth header:
curl -s https://api.getroomi.com/api/projects \
-H "$(pitch auth header | jq -r '"Authorization: " + .Authorization')"The token is your session: it is as good as your password until it expires
or you run pitch logout, which ends it on the platform. Keep it out of files, logs and
the repository. A token the connector was issued for Claude is refused here,
and a token from pitch login is refused by the connector: each is good for
one thing only.
Workspaces
Every endpoint acts on the active workspace of the session: the workspace the session has active, or, for a session that has none, the first workspace the person joined. There is no workspace in the path. A person who belongs to no workspace is treated as signed out.
An id from another workspace is a 404, exactly as an id that does not
exist, so the API never says whether something exists elsewhere.
Requests and replies
- Bodies are JSON. Send
Content-Type: application/json. A body that does not fit the endpoint's schema is refused with400,{"error":"invalid"}, and anissueslist naming each field that does not fit. Fields an endpoint does not name are ignored. - Replies are JSON, checked against the contract before they leave, so a reply always has the shape listed below.
- Status codes. A
GETanswers200. APOSTthat creates something (createProject,createBuild,createLink) answers201; otherPOSTs answer200. The upload'sPUTanswers204with no body. - Ids are strings, UUIDs today. Store them as they are; do not rely on their format.
- Times are ISO 8601 strings in UTC, such as
2026-09-28T14:03:11.000Z. A time that does not apply, such as the expiry of a link that never expires, isnull. - Days in the analytics paths are UTC dates written
YYYY-MM-DD, both ends included. - Lists are returned whole. There is no pagination.
Errors
A refusal is a status and a JSON body whose error says what, in words you
can show a person, and usually a reason a program can act on:
{ "error": "refused: taken", "reason": "taken" }| Status | Means |
|---|---|
400 |
The body does not fit the schema (invalid, with issues). |
401 |
Not signed in (sign in first). |
402 |
A plan's limit. The body names the limit, the plan, and the plan that would allow more as upgrade. |
404 |
Not found in your workspace (not found). |
409 |
The name is taken (reason: taken). |
411 |
An upload was sent without Content-Length. |
422 |
Refused on its merits (refused: <reason>). |
423 |
The project or workspace is paused (reason: paused). |
429 |
Too many reports in a day (reason: rate). |
500 |
The platform's own fault (internal error). |
503 |
A feature that is not switched on (reason: unavailable). |
Errors lists every reason and what to do about
it.
Limits
The API has no request rate limit of its own. What limits you is your plan:
the number of projects, deploys in any 24 hours, private links, viewer-hours
and analytics, each refused with a 402 that says which. The largest upload
the platform accepts is 512 MB. See Plans and limits.
Deploying through the API
A build is uploaded in three calls, then made live. This is what pitch deploy --commit does, and the deploy_static tool.
1. Start an upload. Declare the build's kind and its exact size in bytes:
POST /api/projects/{projectId}/uploads
Content-Type: application/json
{"kind":"static","sizeBytes":20480}{ "uploadId": "e4b7c2a1-9f3d-4c68-a5e1-0d8b6f2c9a37", "putUrl": "/api/uploads/e4b7c2a1-9f3d-4c68-a5e1-0d8b6f2c9a37" }2. Send the bytes to putUrl, on the API's own address, signed in the
same way. Content-Length must be the size you declared; what is stored is
measured again and refused if it differs. An upload takes its bytes once.
PUT /api/uploads/e4b7c2a1-9f3d-4c68-a5e1-0d8b6f2c9a37
Content-Type: application/gzip
Content-Length: 20480It answers 204.
3. Register the build with the upload and its pitch.json:
POST /api/projects/{projectId}/builds
Content-Type: application/json
{"uploadId":"e4b7c2a1-9f3d-4c68-a5e1-0d8b6f2c9a37","manifest":{…},"note":"New onboarding flow"}It answers 201 with the build, which is not live yet. A static build is
unpacked here, and refused if its archive is not one the platform will serve.
The deploy counts against your plan's deploys at this step.
4. Make it live:
POST /api/projects/{projectId}/builds/{buildId}/liveThe same call makes an earlier build live again, which is how a rollback is done.
What the artefact is:
- A static build: a gzipped tar of the built folder, with
index.htmlat its root. At most 5,000 files, 32 MB a file and 256 MB unpacked; files and folders only, no links. - A container build: the bundle
pitch deploymakes from your image. Its manifest must carryrun, the start commandpitch deployreads from the image, so a container build is deployed withpitch deploy, not by hand.
Endpoints
Every endpoint, drawn from the contract the platform checks each request and
reply against. Paths show parameters as :name.
Projects, builds and links
The core of the contract: projects, uploads and builds, viewer links, and what viewers did and said. pitch and the connector call these.
listProjects
GET /api/projects
Every project in your workspace that has not been deleted, newest first, each with its room’s address. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
slug |
string |
required · 3–40 characters |
name |
string |
required |
visibility |
"private" | "public" |
required |
liveBuildId |
string | null |
required |
roomUrl |
string (uri) |
required |
createdAt |
string (date-time) |
required |
createProject
POST /api/projects
Creates a project. Its slug is the room’s address; its name is what the room shows. Refused with 409 when the slug is taken or was deleted less than 30 days ago, 422 when it is not one the platform allows (format, reserved, impersonation, banned), and 402 beyond the plan’s projects.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
slug |
string |
required · 3–40 characters |
name |
string |
required · 1–80 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
slug |
string |
required · 3–40 characters |
name |
string |
required |
visibility |
"private" | "public" |
required |
liveBuildId |
string | null |
required |
roomUrl |
string (uri) |
required |
createdAt |
string (date-time) |
required |
getProject
GET /api/projects/:projectId
One project of your workspace, by id. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
slug |
string |
required · 3–40 characters |
name |
string |
required |
visibility |
"private" | "public" |
required |
liveBuildId |
string | null |
required |
roomUrl |
string (uri) |
required |
createdAt |
string (date-time) |
required |
startUpload
POST /api/projects/:projectId/uploads
Declares a build’s artefact before it is sent: its kind and its exact size in bytes, at most 512 MB. Returns the upload’s id and the address to PUT the bytes to, with Content-Length equal to the size declared; that PUT answers 204, and is refused if the size differs.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
kind |
"container" | "static" |
required |
sizeBytes |
integer |
required |
Reply: object
| Field | Type | |
|---|---|---|
uploadId |
string |
required · not empty |
putUrl |
string |
required |
createBuild
POST /api/projects/:projectId/builds
Registers a build from a finished upload on the same project, with its pitch.json. The build is not live until makeLive. A static build is unpacked here; a container build must carry run. Refused as unfinished, kind, used or archive, with 402 beyond the plan’s deploys and 423 while the platform has the project paused. rehearsal, optional, is what the deploy saw rehearsing each story step; the app shows a build without one as not reported, never as passed.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
uploadId |
string |
required · not empty |
manifest |
object |
required |
note |
string |
optional · at most 200 characters |
rehearsal |
object |
optional |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
projectId |
string |
required · not empty |
kind |
"container" | "static" |
required |
manifest |
object |
required |
sizeBytes |
integer |
required |
note |
string | null |
required |
live |
boolean |
required |
createdAt |
string (date-time) |
required |
listBuilds
GET /api/projects/:projectId/builds
Every build of the project, newest first, each saying whether it is the live one. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
projectId |
string |
required · not empty |
kind |
"container" | "static" |
required |
manifest |
object |
required |
sizeBytes |
integer |
required |
note |
string | null |
required |
live |
boolean |
required |
createdAt |
string (date-time) |
required |
makeLive
POST /api/projects/:projectId/builds/:buildId/live
Makes one of the project’s builds the live one, the build its room shows. The same call makes an earlier build live again, which is a rollback. Who did it and when is recorded.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId, buildId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
projectId |
string |
required · not empty |
kind |
"container" | "static" |
required |
manifest |
object |
required |
sizeBytes |
integer |
required |
note |
string | null |
required |
live |
boolean |
required |
createdAt |
string (date-time) |
required |
createLink
POST /api/projects/:projectId/links
Makes a private link to the room for one named viewer, with their email if you give it (nothing is sent to it) and an expiry of 1 to 365 days if you want one. The reply carries the link’s full address; no list does, and revealLink returns it again. Refused with 402 on a plan without private links, beyond the plan’s links per project, or once the month’s viewer-hours are used.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
viewerName |
string |
required · 1–80 characters |
viewerEmail |
string (email) |
optional |
expiresInDays |
integer |
optional · 1–365 |
Reply: object
| Field | Type | |
|---|---|---|
link |
object |
required |
url |
string (uri) |
required |
listLinks
GET /api/projects/:projectId/links
Every link of the project, each with its stage in the pipeline and whether its address can be shown again (recoverable), never the address. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
revokeLink
POST /api/links/:linkId/revoke
Revokes a link: it stops opening the room at once, and cannot be opened again. What its viewer already did stays on record.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
revealLink
POST /api/links/:linkId/reveal
A link’s full address again, to copy and resend when the viewer has lost it. Recorded in the project’s history with who asked. Refused with 422 as revoked or expired when the link no longer opens the room, and as unrecoverable for a link made before 1 October 2026, when only a hash of its key was kept: reissue that one. A connection that may only read is refused with 403, because the address opens the room. Sent with Cache-Control: no-store.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
link |
object |
required |
url |
string (uri) |
required |
reissueLink
POST /api/links/:linkId/reissue
A new address for the same link: the same viewer, with their visits, answers and comments, under a new key. The old address stops opening the room the moment it lands. For a link made before addresses were kept, or one that has gone astray; refused as revoked or expired like revealLink. Recorded in the project’s history.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
link |
object |
required |
url |
string (uri) |
required |
listVisits
GET /api/projects/:projectId/visits
Every visit to the project’s room, with the seconds spent on each app and page. A preview records none. Solo and up: on Free it is refused with 402, as each viewer’s journey is, and total views are on the analytics endpoints. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string | null |
required |
viewerName |
string | null |
required |
startedAt |
string (date-time) |
required |
endedAt |
string (date-time) | null |
required |
time |
{ [key]: integer } |
required |
listResponses
GET /api/projects/:projectId/responses
Every answer viewers gave to “What happens next”, with the detail they added. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string |
required · not empty |
viewerName |
string |
required |
step |
"meeting" | "yes" | "later" | "colleague" | "question" | "no" |
required |
detail |
{ [key]: any } | null |
required |
createdAt |
string (date-time) |
required |
listComments
GET /api/projects/:projectId/comments
Every comment viewers left in the project’s room, with the app and screen it was pinned to and whether it is resolved. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string | null |
required |
viewerName |
string | null |
required |
appId |
string | null |
required |
screen |
string | null |
required |
body |
string |
required |
resolvedAt |
string (date-time) | null |
required |
createdAt |
string (date-time) |
required |
position |
object | null |
required |
area |
object | null |
required |
device |
"phone" | "tablet" | "laptop" | "screen" | "none" | null |
required |
hasScreenshot |
boolean |
required |
pipeline
GET /api/pipeline
Every viewer link across every project in the workspace, with its stage: sent, opened, engaged, yes, later or no. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
projectId |
string |
required · not empty |
projectName |
string |
required |
addPage
POST /api/projects/:projectId/pages
Adds a Markdown or HTML page to the project’s room, which every viewer sees at once. A page with the title of one the room already has replaces that page’s content.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
title |
string |
required · 1–80 characters |
kind |
"markdown" | "html" |
required |
content |
string |
required · at most 2000000 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
title |
string |
required |
kind |
"markdown" | "html" |
required |
listWorkspaces
GET /api/workspaces
Every workspace you belong to: your own first, then each team, with your role in each (owner or member), which one this session is working in (active), and the brand its plan shows (brand: its name and whether it has a logo, read from GET /api/workspaces/:workspaceId/logo; null for none). Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
name |
string |
required |
kind |
"personal" | "team" |
required |
role |
"owner" | "member" |
required |
active |
boolean |
required |
brand |
object | null |
optional |
createTeam
POST /api/workspaces
Makes a team with the name given, with you as its owner, and answers it. A new team has no plan, so it holds no projects and nobody else until it has Studio or Agency; you may have one such team at a time (422, reason: unlicensed). Your own workspace is unchanged.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
name |
string |
required · 1–80 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
name |
string |
required |
kind |
"personal" | "team" |
required |
role |
"owner" | "member" |
required |
active |
boolean |
required |
brand |
object | null |
optional |
listMembers
GET /api/workspaces/:workspaceId/members
Everyone in a workspace you belong to, owners first, each with their name, email, picture, role and when they joined; you marks you. Your own workspace has only you. Changes nothing; another workspace is a 404.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
userId |
string |
required · not empty |
displayName |
string |
required |
email |
string |
required |
image |
string | null |
required |
role |
"owner" | "member" |
required |
joinedAt |
string (date-time) |
required |
you |
boolean |
required |
setMemberRole
POST /api/workspaces/:workspaceId/members/:userId/role
Makes someone in a team an owner or a member. An owner’s to do (403, reason: permission, for a member), and refused (422, reason: team) when it would leave the team with no owner. Answers the team’s people.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId, userId
Body: object
| Field | Type | |
|---|---|---|
role |
"owner" | "member" |
required |
Reply: object[]
| Field | Type | |
|---|---|---|
userId |
string |
required · not empty |
displayName |
string |
required |
email |
string |
required |
image |
string | null |
required |
role |
"owner" | "member" |
required |
joinedAt |
string (date-time) |
required |
you |
boolean |
required |
removeMember
POST /api/workspaces/:workspaceId/members/:userId/remove
Takes someone out of a team at once: from their next request they reach none of it. An owner’s to do; never yourself, which is leaveTeam. Answers the team’s people.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId, userId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
userId |
string |
required · not empty |
displayName |
string |
required |
email |
string |
required |
image |
string | null |
required |
role |
"owner" | "member" |
required |
joinedAt |
string (date-time) |
required |
you |
boolean |
required |
leaveTeam
POST /api/workspaces/:workspaceId/leave
Takes you out of a team. An owner leaves only while another owner stays (422, reason: team), and your own workspace is never left.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
left |
true |
required |
renameWorkspace
POST /api/workspaces/:workspaceId/name
Renames a team, or your own workspace. An owner’s to do. Your own workspace’s name is what a room shows its viewers for a link made before display names.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId
Body: object
| Field | Type | |
|---|---|---|
name |
string |
required · 1–80 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
name |
string |
required |
kind |
"personal" | "team" |
required |
role |
"owner" | "member" |
required |
active |
boolean |
required |
brand |
object | null |
optional |
listInvitations
GET /api/workspaces/:workspaceId/invitations
A team’s invitations still waiting and not expired, each with its email, who made it and when it expires. An owner’s to see. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
email |
string |
required |
role |
"owner" | "member" |
required |
invitedBy |
string |
required |
createdAt |
string (date-time) |
required |
expiresAt |
string (date-time) |
required |
createInvitation
POST /api/workspaces/:workspaceId/invitations
Invites one email into a team and answers the invitation with url, the link to send: nothing is emailed. Only someone signed in with that email, verified as theirs, can use the link, once, within 7 days. An owner’s to do; a waiting invitation holds a seat, so a team at its plan’s seats is refused (422, reason: team), and so is someone in the team already or invited already.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId
Body: object
| Field | Type | |
|---|---|---|
email |
string (email) |
required · at most 254 characters |
Reply: object
| Field | Type | |
|---|---|---|
invitation |
object |
required |
url |
string (uri) |
required |
revokeInvitation
POST /api/workspaces/:workspaceId/invitations/:invitationId/revoke
Revokes an invitation still waiting: its link stops working at once. An owner’s to do. Answers the invitations still waiting.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId, invitationId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
email |
string |
required |
role |
"owner" | "member" |
required |
invitedBy |
string |
required |
createdAt |
string (date-time) |
required |
expiresAt |
string (date-time) |
required |
viewInvitation
GET /api/invitations/:invitationId
An invitation, told only to the person signed in with the email it is for: the team’s name and who invited them. A used, revoked or expired invitation, or anyone else’s, is a 404. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: invitationId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
workspaceId |
string |
required · not empty |
workspaceName |
string |
required |
invitedBy |
string |
required |
expiresAt |
string (date-time) |
required |
acceptInvitation
POST /api/invitations/:invitationId/accept
Joins the team the invitation is for, as a member, and answers the team. Only the invited person, once, while a seat is free (422, reason: team, when none is). The joining is written in the team’s record.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: invitationId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
name |
string |
required |
kind |
"personal" | "team" |
required |
role |
"owner" | "member" |
required |
active |
boolean |
required |
brand |
object | null |
optional |
deleteTeam
POST /api/workspaces/:workspaceId/delete
Deletes a team, with its name typed again as confirm: its name, brand, people and invitations. An owner’s to do, and only once it holds no live project and its plan is cancelled (422, reason: team). Everyone in it keeps their own workspace.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: workspaceId
Body: object
| Field | Type | |
|---|---|---|
confirm |
string |
required |
Reply: object
| Field | Type | |
|---|---|---|
deleted |
true |
required |
projectHistory
GET /api/projects/:projectId/history
Every pause, change of note and resume on the project, and every link copied again (link.copy) or reissued (link.reissue) with whose it was, newest first, with who did it while they are a member. Append-only. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
kind |
"room.pause" | "room.message" | "room.resume" | "link.copy" | "link.reissue" | "room.busy" |
required |
by |
string | null |
required |
message |
string | null |
required |
viewer |
string | null |
required |
until |
string (date-time) | null |
required |
at |
string (date-time) |
required |
Changing a project
Renaming and deleting a project, the room’s order and its pages, and answering feedback.
renameProject
POST /api/projects/:projectId/name
Changes the project’s name, as the room and the app show it. The slug, and so the room’s address, stays as it is.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
name |
string |
required · 1–80 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
slug |
string |
required · 3–40 characters |
name |
string |
required |
visibility |
"private" | "public" |
required |
liveBuildId |
string | null |
required |
roomUrl |
string (uri) |
required |
createdAt |
string (date-time) |
required |
deleteProject
POST /api/projects/:projectId/delete
Deletes the project. confirm must be the project’s slug, or it is refused as confirm. Its links stop opening the room, and its slug is held for 30 days.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
confirm |
string |
required |
Reply: object
| Field | Type | |
|---|---|---|
deleted |
true |
required |
getRoomSetup
GET /api/projects/:projectId/room
The room’s order: the apps as you ordered them (null for pitch.json’s order), and the pages as the rail lists them. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
appOrder |
string[] | null |
required |
pages |
object[] |
required |
orderApps
POST /api/projects/:projectId/room/apps
Sets the order the room’s switcher lists the apps in. The apps it names come first, in its order, then any it leaves out, in pitch.json’s; an id the live build does not have is ignored.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
ids |
string[] |
required · at most 100 items |
Reply: object
| Field | Type | |
|---|---|---|
appOrder |
string[] | null |
required |
pages |
object[] |
required |
orderPages
POST /api/projects/:projectId/room/pages
Sets the order the rail lists the pages in. The list must name every page of the project exactly once, or it is refused as order.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
ids |
string[] |
required · at most 100 items |
Reply: object
| Field | Type | |
|---|---|---|
appOrder |
string[] | null |
required |
pages |
object[] |
required |
removePage
POST /api/projects/:projectId/pages/:pageId/remove
Takes a page out of the room and deletes its content. The pages after it close ranks. It is uploaded again by the next deploy if pitch.json still lists it.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId, pageId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
appOrder |
string[] | null |
required |
pages |
object[] |
required |
listFeedback
GET /api/projects/:projectId/feedback
Every comment on the project, with your reply to it and whether it is resolved. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string | null |
required |
viewerName |
string | null |
required |
appId |
string | null |
required |
screen |
string | null |
required |
body |
string |
required |
resolvedAt |
string (date-time) | null |
required |
createdAt |
string (date-time) |
required |
position |
object | null |
required |
area |
object | null |
required |
device |
"phone" | "tablet" | "laptop" | "screen" | "none" | null |
required |
hasScreenshot |
boolean |
required |
reply |
string | null |
required |
repliedAt |
string (date-time) | null |
required |
from |
object | null |
required |
resolveComment
POST /api/comments/:commentId/resolve
Marks a comment resolved, or, with false, not resolved again.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: commentId
Body: object
| Field | Type | |
|---|---|---|
resolved |
boolean |
required |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string | null |
required |
viewerName |
string | null |
required |
appId |
string | null |
required |
screen |
string | null |
required |
body |
string |
required |
resolvedAt |
string (date-time) | null |
required |
createdAt |
string (date-time) |
required |
position |
object | null |
required |
area |
object | null |
required |
device |
"phone" | "tablet" | "laptop" | "screen" | "none" | null |
required |
hasScreenshot |
boolean |
required |
reply |
string | null |
required |
repliedAt |
string (date-time) | null |
required |
from |
object | null |
required |
replyToComment
POST /api/comments/:commentId/reply
Answers a viewer’s comment; the viewer sees the answer in the room. One answer per comment: answering again replaces it.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: commentId
Body: object
| Field | Type | |
|---|---|---|
body |
string |
required · 1–2000 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string | null |
required |
viewerName |
string | null |
required |
appId |
string | null |
required |
screen |
string | null |
required |
body |
string |
required |
resolvedAt |
string (date-time) | null |
required |
createdAt |
string (date-time) |
required |
position |
object | null |
required |
area |
object | null |
required |
device |
"phone" | "tablet" | "laptop" | "screen" | "none" | null |
required |
hasScreenshot |
boolean |
required |
reply |
string | null |
required |
repliedAt |
string (date-time) | null |
required |
from |
object | null |
required |
Pausing a room
Closing every link to a project’s room for now, with a note to viewers, opening it again, and the project’s history of both.
getRoomPause
GET /api/projects/:projectId/pause
Where the room stands: your pause (when, by whom, and the note to viewers), any pause of the platform’s (over-limit, suspended, takedown), and what a viewer with a working link meets now: room, paused or unavailable. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
owner |
object | null |
required |
system |
"over-limit" | "suspended" | "takedown" | null |
required |
viewers |
"room" | "paused" | "unavailable" |
required |
pauseRoom
POST /api/projects/:projectId/pause
Pauses the room: every working link shows a page saying who paused it, with message under it (at most 280 characters, or null for none), and no demo starts. Links, builds and pages are untouched, and deploys and new links still work. Called on a paused room, it changes the note. Recorded in the project’s history.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
message |
string | null |
required |
Reply: object
| Field | Type | |
|---|---|---|
owner |
object | null |
required |
system |
"over-limit" | "suspended" | "takedown" | null |
required |
viewers |
"room" | "paused" | "unavailable" |
required |
resumeRoom
POST /api/projects/:projectId/resume
Lifts your pause, so the same links open the room again. A pause of the platform’s stays, and the reply says so. Refused as taken when the room is not paused. Recorded in the project’s history.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
owner |
object | null |
required |
system |
"over-limit" | "suspended" | "takedown" | null |
required |
viewers |
"room" | "paused" | "unavailable" |
required |
projectHistory
GET /api/projects/:projectId/history
Every pause, change of note and resume on the project, and every link copied again (link.copy) or reissued (link.reissue) with whose it was, newest first, with who did it while they are a member. Append-only. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
kind |
"room.pause" | "room.message" | "room.resume" | "link.copy" | "link.reissue" | "room.busy" |
required |
by |
string | null |
required |
message |
string | null |
required |
viewer |
string | null |
required |
until |
string (date-time) | null |
required |
at |
string (date-time) |
required |
The tour
The live build’s story, step by step, beside what its deploy reported of rehearsing each step.
getTour
GET /api/projects/:projectId/tour
The live build’s story, step by step: each step’s app, its calls, and how its rehearsal went (passed, failed, nothing-to-run or not-reported), with what the demo last said. build is null when nothing is live. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
build |
object | null |
required |
steps |
object[] |
required |
reported |
boolean |
required |
Analytics
Build history, each viewer’s journey through the room, and analytics over a range of days.
buildHistory
GET /api/projects/:projectId/builds/history
Every build of the project, newest first, with who deployed it and each time it was made live, so the app can show what was live when. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
projectId |
string |
required · not empty |
kind |
"container" | "static" |
required |
manifest |
object |
required |
sizeBytes |
integer |
required |
note |
string | null |
required |
live |
boolean |
required |
createdAt |
string (date-time) |
required |
deployedBy |
object | null |
required |
releases |
object[] |
required |
journey
GET /api/links/:linkId/journey
One link’s visits in order: what the viewer opened, ran, said and chose, where they stopped, and how far they took the tour, with everyone who opened the link. Refused with 402 on a plan without analytics in full. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
link |
object |
required |
people |
object[] |
required |
sessions |
object[] |
required |
outside |
object[] |
required |
tour |
object | null |
required |
windowStart |
string (date-time) | null |
optional |
projectAnalytics
GET /api/projects/:projectId/analytics/:from/:to
One project’s analytics over a range of UTC days, written YYYY-MM-DD, both ends included, at most 366 days. In full on a plan with it; total views only on one without. A range that is not days, or ends before it starts, is refused as range.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId, from, to
Body: none
Reply: object
workspaceAnalytics
GET /api/analytics/:from/:to
The same as projectAnalytics, across every project in the workspace, with the split by project.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: from, to
Body: none
Reply: object
The pipeline
Each viewer as a card to work: placed in a stage, judged, followed up and noted. Solo and up: on Free every endpoint here is refused with 402.
board
GET /api/board
Every link on every project that has not been deleted, as a card: its stage, the stage the evidence suggests, the outcome, the follow-up, your notes and the signals behind it. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
projectId |
string |
required · not empty |
projectName |
string |
required |
suggested |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
placed |
boolean |
required |
outcome |
"won" | "lost" | "later" | null |
required |
followUp |
object |
required |
notes |
integer |
required |
signals |
object |
required |
forwarded |
object[] |
required |
claims |
integer |
required |
projectBoard
GET /api/projects/:projectId/board
One project’s links, as cards. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
projectId |
string |
required · not empty |
projectName |
string |
required |
suggested |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
placed |
boolean |
required |
outcome |
"won" | "lost" | "later" | null |
required |
followUp |
object |
required |
notes |
integer |
required |
signals |
object |
required |
forwarded |
object[] |
required |
claims |
integer |
required |
placeLink
POST /api/links/:linkId/stage
Places a viewer in a stage by hand, over what the evidence says; the evidence’s stage is still offered as a suggestion. null hands them back to the evidence.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: object
| Field | Type | |
|---|---|---|
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" | null |
required |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
projectId |
string |
required · not empty |
projectName |
string |
required |
suggested |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
placed |
boolean |
required |
outcome |
"won" | "lost" | "later" | null |
required |
followUp |
object |
required |
notes |
integer |
required |
signals |
object |
required |
forwarded |
object[] |
required |
claims |
integer |
required |
judgeLink
POST /api/links/:linkId/outcome
Marks the deal won, lost or later. null clears it.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: object
| Field | Type | |
|---|---|---|
outcome |
"won" | "lost" | "later" | null |
required |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
projectId |
string |
required · not empty |
projectName |
string |
required |
suggested |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
placed |
boolean |
required |
outcome |
"won" | "lost" | "later" | null |
required |
followUp |
object |
required |
notes |
integer |
required |
signals |
object |
required |
forwarded |
object[] |
required |
claims |
integer |
required |
followUp
POST /api/links/:linkId/follow-up
Sets the day to follow this viewer up, and a line on what about. Both null clears it. Nothing is sent to anyone.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: object
| Field | Type | |
|---|---|---|
on |
string | null |
required |
note |
string | null |
required |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
viewerName |
string |
required |
viewerEmail |
string (email) | null |
required |
expiresAt |
string (date-time) | null |
required |
revokedAt |
string (date-time) | null |
required |
stage |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
recoverable |
boolean |
optional |
projectId |
string |
required · not empty |
projectName |
string |
required |
suggested |
"sent" | "opened" | "engaged" | "yes" | "later" | "no" |
required |
placed |
boolean |
required |
outcome |
"won" | "lost" | "later" | null |
required |
followUp |
object |
required |
notes |
integer |
required |
signals |
object |
required |
forwarded |
object[] |
required |
claims |
integer |
required |
listNotes
GET /api/links/:linkId/notes
Your workspace’s notes on one viewer, newest first. They are never shown in the room. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string |
required · not empty |
body |
string |
required |
by |
string | null |
required |
byImage |
string | null |
required |
createdAt |
string (date-time) |
required |
addNote
POST /api/links/:linkId/notes
Adds a note on one viewer, and returns all of their notes. Notes are never shown in the room.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: linkId
Body: object
| Field | Type | |
|---|---|---|
body |
string |
required · 1–2000 characters |
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string |
required · not empty |
body |
string |
required |
by |
string | null |
required |
byImage |
string | null |
required |
createdAt |
string (date-time) |
required |
removeNote
POST /api/notes/:noteId/remove
Removes one note, and returns the link’s remaining notes.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: noteId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string |
required · not empty |
body |
string |
required |
by |
string | null |
required |
byImage |
string | null |
required |
createdAt |
string (date-time) |
required |
Feedback
One comment a viewer left, in full.
getFeedback
GET /api/comments/:commentId
One comment in full: where on the stage it was pinned, the app’s name, whether a screenshot came with it, your reply, and the viewer who left it with their stage. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: commentId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
linkId |
string | null |
required |
viewerName |
string | null |
required |
appId |
string | null |
required |
screen |
string | null |
required |
body |
string |
required |
resolvedAt |
string (date-time) | null |
required |
createdAt |
string (date-time) |
required |
position |
object | null |
required |
area |
object | null |
required |
device |
"phone" | "tablet" | "laptop" | "screen" | "none" | null |
required |
hasScreenshot |
boolean |
required |
reply |
string | null |
required |
repliedAt |
string (date-time) | null |
required |
from |
object | null |
required |
projectId |
string |
required · not empty |
appName |
string | null |
required |
screenshot |
object | null |
required |
screenshotNote |
"removed" | "no-answer" | "timeout" | "blocked" | "tainted" | "too-large" | "failed" | null |
required |
viewer |
object | null |
required |
thread |
object[] |
required |
Room setup
The next steps a room offers, the workspace’s brand, and your own community profile.
getNextSteps
GET /api/projects/:projectId/next-steps
The next steps the project’s room offers at the end of a visit, as you set them, whether the plan lets you choose, and what the room offers now. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
steps |
object[] |
required · at most 6 items |
removed |
object[] |
required |
planAllows |
boolean |
required |
offered |
"meeting" | "yes" | "later" | "colleague" | "question" | "no"[] |
required |
setNextSteps
POST /api/projects/:projectId/next-steps
Sets which next steps the room offers, in what order and in what words. A step left out of steps is deleted: never offered, its words kept in removed; sending it again adds it back. On a plan that cannot choose, the setting is kept, and the room offers only a question.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
steps |
object[] |
required · at most 6 items |
Reply: object
| Field | Type | |
|---|---|---|
steps |
object[] |
required · at most 6 items |
removed |
object[] |
required |
planAllows |
boolean |
required |
offered |
"meeting" | "yes" | "later" | "colleague" | "question" | "no"[] |
required |
getBrand
GET /api/brand
Your workspace’s brand: its name, accent colour and logo, and whether the plan shows it in the room. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
name |
string | null |
required |
accent |
string | null |
required |
logo |
object | null |
required |
workspaceName |
string |
required |
planAllows |
boolean |
required |
problems |
object[] |
optional |
setBrand
POST /api/brand
Sets the brand’s name and accent colour (#rrggbb); null clears either. On a plan without its own branding, rooms carry “Made with Roomi” instead. An accent a viewer could not read in the room is refused with 400, reason: contrast, and problems saying each pair that fails and a shade that would pass. The logo is uploaded with PUT /api/brand/logo: a PNG, JPEG or WebP image of up to 256 KB, from 16 × 16 to 2048 × 2048 pixels, and no more than 8 times as wide as it is tall.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
name |
string | null |
required |
accent |
string | null |
required |
Reply: object
| Field | Type | |
|---|---|---|
name |
string | null |
required |
accent |
string | null |
required |
logo |
object | null |
required |
workspaceName |
string |
required |
planAllows |
boolean |
required |
problems |
object[] |
optional |
removeLogo
POST /api/brand/logo/remove
Removes the brand’s logo.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
name |
string | null |
required |
accent |
string | null |
required |
logo |
object | null |
required |
workspaceName |
string |
required |
planAllows |
boolean |
required |
problems |
object[] |
optional |
getTheme
GET /api/brand/theme
Your workspace’s room theme: its tokens as saved (null for none), whether the plan shows it, the first plan that does, every colour and value the room draws for it with your accent, and anything in it a viewer could not read now. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
theme |
object | null |
required |
planAllows |
boolean |
required |
shownOn |
string | null |
required |
effective |
{ [key]: string } |
required |
problems |
object[] |
required |
fellBack |
string[] |
required |
setTheme
POST /api/brand/theme
Saves the room theme whole: a token left out is the room’s own. Colours are #rrggbb; radius is square, soft or round; font is system, serif or rounded. A theme a viewer could not read is refused with 400, reason: contrast, and its problems. Kept whatever the plan, and shown on a plan that includes it.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
theme |
object |
required |
Reply: object
| Field | Type | |
|---|---|---|
theme |
object | null |
required |
planAllows |
boolean |
required |
shownOn |
string | null |
required |
effective |
{ [key]: string } |
required |
problems |
object[] |
required |
fellBack |
string[] |
required |
resetTheme
POST /api/brand/theme/reset
Removes the room theme: rooms are drawn in the room’s own look again.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
theme |
object | null |
required |
planAllows |
boolean |
required |
shownOn |
string | null |
required |
effective |
{ [key]: string } |
required |
problems |
object[] |
required |
fellBack |
string[] |
required |
checkTheme
POST /api/brand/theme/check
Checks a room theme, with another accent if you give one, and says what the room would draw and what would be refused. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
theme |
object |
required |
accent |
string | null |
optional |
Reply: object
| Field | Type | |
|---|---|---|
problems |
object[] |
required |
effective |
{ [key]: string } |
required |
getProfile
GET /api/me/profile
Your own community handle and bio. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
handle |
string | null |
required |
bio |
string | null |
required |
setProfile
POST /api/me/profile
Sets your community handle and bio. The handle follows a project address’s rules and is unique among people: refused as format, reserved, impersonation, banned or taken.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
handle |
string |
required · 3–40 characters |
bio |
string | null |
required |
Reply: object
| Field | Type | |
|---|---|---|
handle |
string | null |
required |
bio |
string | null |
required |
The community
A project’s community card, listing and unlisting it, the room’s access rule, and the one-time passes into a room.
getCard
GET /api/projects/:projectId/card
The project’s community card: its title, line and cover, where its listing stands (draft, listed, blocked with what a check found, pulled with Roomi's comment, or pending while it waits for review), whether a new cover is still held back, whether a review can be asked for, the public address it would have, and whether suggested wording is available. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
projectId |
string |
required · not empty |
title |
string |
required |
line |
string |
required |
cover |
object | null |
required |
status |
"draft" | "listed" | "blocked" | "pulled" | "pending" |
required |
rejectReason |
string | null |
required |
submittedAt |
string (date-time) | null |
required |
reviewedAt |
string (date-time) | null |
required |
warnings |
object[] |
required |
blocked |
object[] |
required |
coverPending |
boolean |
required |
hidden |
boolean |
required |
appealable |
boolean |
required |
appeal |
object | null |
required |
publicUrl |
string (uri) |
required |
suggest |
boolean |
required |
updatedAt |
string (date-time) | null |
required |
saveCard
POST /api/projects/:projectId/card
Saves the card’s title and line. A listed card’s new words are checked first: it stays listed if they pass, and is blocked (off the community) if a check fails. At most 10 such edits a day; past that, 429. Any other card keeps its state. The cover is uploaded with PUT /api/projects/:projectId/card/cover: a PNG, JPEG or WebP image of up to 1 MB, once the card exists; a new cover is held back from the community until its own check passes.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
title |
string |
required · 1–60 characters |
line |
string |
required · 1–140 characters |
Reply: object
| Field | Type | |
|---|---|---|
projectId |
string |
required · not empty |
title |
string |
required |
line |
string |
required |
cover |
object | null |
required |
status |
"draft" | "listed" | "blocked" | "pulled" | "pending" |
required |
rejectReason |
string | null |
required |
submittedAt |
string (date-time) | null |
required |
reviewedAt |
string (date-time) | null |
required |
warnings |
object[] |
required |
blocked |
object[] |
required |
coverPending |
boolean |
required |
hidden |
boolean |
required |
appealable |
boolean |
required |
appeal |
object | null |
required |
publicUrl |
string (uri) |
required |
suggest |
boolean |
required |
updatedAt |
string (date-time) | null |
required |
removeCover
POST /api/projects/:projectId/card/cover/remove
Removes the card’s cover.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
projectId |
string |
required · not empty |
title |
string |
required |
line |
string |
required |
cover |
object | null |
required |
status |
"draft" | "listed" | "blocked" | "pulled" | "pending" |
required |
rejectReason |
string | null |
required |
submittedAt |
string (date-time) | null |
required |
reviewedAt |
string (date-time) | null |
required |
warnings |
object[] |
required |
blocked |
object[] |
required |
coverPending |
boolean |
required |
hidden |
boolean |
required |
appealable |
boolean |
required |
appeal |
object | null |
required |
publicUrl |
string (uri) |
required |
suggest |
boolean |
required |
updatedAt |
string (date-time) | null |
required |
checkBuildCover
POST /api/projects/:projectId/card/cover/build/check
Reads an image in the project’s live build, by its path from the build’s root, as the card’s cover would take it, and returns its type and size in bytes. The type is read from the file’s bytes, never its name: a PNG, JPEG or WebP image, at most 1 MB, or it is refused as file. A container build is never unpacked, so its files are refused too. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
path |
string |
required · 1–255 characters |
Reply: object
| Field | Type | |
|---|---|---|
path |
string |
required |
type |
"image/png" | "image/jpeg" | "image/webp" |
required |
bytes |
integer |
required |
coverFromBuild
POST /api/projects/:projectId/card/cover/build
Makes an image in the project’s live build the card’s cover, checked as checkBuildCover checks it, and copied on the platform: nothing is uploaded, and the cover is stored and served from the card’s row like an uploaded one. Needs a saved card. Held back from the community until its own check passes, as with an upload.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
path |
string |
required · 1–255 characters |
Reply: object
| Field | Type | |
|---|---|---|
projectId |
string |
required · not empty |
title |
string |
required |
line |
string |
required |
cover |
object | null |
required |
status |
"draft" | "listed" | "blocked" | "pulled" | "pending" |
required |
rejectReason |
string | null |
required |
submittedAt |
string (date-time) | null |
required |
reviewedAt |
string (date-time) | null |
required |
warnings |
object[] |
required |
blocked |
object[] |
required |
coverPending |
boolean |
required |
hidden |
boolean |
required |
appealable |
boolean |
required |
appeal |
object | null |
required |
publicUrl |
string (uri) |
required |
suggest |
boolean |
required |
updatedAt |
string (date-time) | null |
required |
listProject
POST /api/projects/:projectId/card/list
Lists the project on the community: runs the pre-flight scan, which warns and never blocks, and Roomi's checks. If they pass it is listed at once; if one fails it is blocked, with the part and the reason. After Roomi took it off before, it waits for review (pending). Refused as unfinished without a card or without a live build; at most 3 a day for a workspace, 429 past that.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
projectId |
string |
required · not empty |
title |
string |
required |
line |
string |
required |
cover |
object | null |
required |
status |
"draft" | "listed" | "blocked" | "pulled" | "pending" |
required |
rejectReason |
string | null |
required |
submittedAt |
string (date-time) | null |
required |
reviewedAt |
string (date-time) | null |
required |
warnings |
object[] |
required |
blocked |
object[] |
required |
coverPending |
boolean |
required |
hidden |
boolean |
required |
appealable |
boolean |
required |
appeal |
object | null |
required |
publicUrl |
string (uri) |
required |
suggest |
boolean |
required |
updatedAt |
string (date-time) | null |
required |
rehearseListing
GET /api/projects/:projectId/card/listing
What asking to list would do now — listed, blocked with its findings, pending, already or refused — and why, with the pre-flight warnings. Runs the checks without the content classifier, and writes and counts nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
outcome |
"listed" | "blocked" | "pending" | "already" | "refused" |
required |
findings |
object[] |
required |
warnings |
object[] |
required |
said |
string[] |
required |
appealListing
POST /api/projects/:projectId/card/appeal
Asks Roomi to review a block or a decision to take the listing off, in your own words (10 to 1,000 characters). One open at a time, at most 3 a workspace in 30 days: 409 while one is open, 429 past the limit. If it is upheld, exactly this card and build are listed.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
message |
string |
required · 10–1000 characters |
Reply: object
| Field | Type | |
|---|---|---|
projectId |
string |
required · not empty |
title |
string |
required |
line |
string |
required |
cover |
object | null |
required |
status |
"draft" | "listed" | "blocked" | "pulled" | "pending" |
required |
rejectReason |
string | null |
required |
submittedAt |
string (date-time) | null |
required |
reviewedAt |
string (date-time) | null |
required |
warnings |
object[] |
required |
blocked |
object[] |
required |
coverPending |
boolean |
required |
hidden |
boolean |
required |
appealable |
boolean |
required |
appeal |
object | null |
required |
publicUrl |
string (uri) |
required |
suggest |
boolean |
required |
updatedAt |
string (date-time) | null |
required |
listNotices
GET /api/notices
Your own notices in this workspace, newest first, the last fifty: a listing taken off, a project taken down, a review decided. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
kind |
"listing.blocked" | "listing.pulled" | "listing.takedown" | "appeal.upheld" | "appeal.denied" |
required |
title |
string |
required |
body |
string |
required |
href |
string | null |
required |
createdAt |
string (date-time) |
required |
readAt |
string (date-time) | null |
required |
readNotices
POST /api/notices/read
Marks your own notices read: the ids given, or all of them. Says how many changed.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
ids |
string[] |
optional · at most 50 items |
Reply: object
| Field | Type | |
|---|---|---|
read |
integer |
required |
scanCard
POST /api/projects/:projectId/card/scan
Runs the pre-flight scan on the project with the title and line given, as asking to list would, and returns its warnings. Changes nothing: the card is not saved and nothing is listed. The Claude connector’s set_community_card and request_listing show it before you agree.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
title |
string |
required · 1–60 characters |
line |
string |
required · 1–140 characters |
Reply: object
| Field | Type | |
|---|---|---|
warnings |
object[] |
required |
unlistProject
POST /api/projects/:projectId/card/unlist
Takes the project off the community at once; its public address stops answering. The card is kept as a draft.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
projectId |
string |
required · not empty |
title |
string |
required |
line |
string |
required |
cover |
object | null |
required |
status |
"draft" | "listed" | "blocked" | "pulled" | "pending" |
required |
rejectReason |
string | null |
required |
submittedAt |
string (date-time) | null |
required |
reviewedAt |
string (date-time) | null |
required |
warnings |
object[] |
required |
blocked |
object[] |
required |
coverPending |
boolean |
required |
hidden |
boolean |
required |
appealable |
boolean |
required |
appeal |
object | null |
required |
publicUrl |
string (uri) |
required |
suggest |
boolean |
required |
updatedAt |
string (date-time) | null |
required |
suggestWording
POST /api/projects/:projectId/card/suggest
Drafts neutral wording for the card with Claude, from the card’s own words and nothing else. Not switched on yet: it answers 503 while no Anthropic key is configured.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
title |
string |
required · 1–60 characters |
line |
string |
required · 1–140 characters |
Reply: object
| Field | Type | |
|---|---|---|
title |
string |
required · 1–60 characters |
line |
string |
required · 1–140 characters |
previewPass
POST /api/projects/:projectId/preview
A one-time address that opens the room exactly as a viewer sees it, marked as a preview, recording nothing. For a member of the project’s workspace; it lives 120 seconds and is spent on arrival.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
url |
string (uri) |
required |
getAccess
GET /api/projects/:projectId/access
Whether the project’s private links ask for an email before the room shows anything, and whether the plan has private links at all. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
requireEmail |
boolean |
required |
privateLinks |
boolean |
required |
setAccess
POST /api/projects/:projectId/access
Sets whether the project’s private links ask the viewer for an email before the room shows anything. Nothing is sent to the email, and it is not verified.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: object
| Field | Type | |
|---|---|---|
requireEmail |
boolean |
required |
Reply: object
| Field | Type | |
|---|---|---|
requireEmail |
boolean |
required |
privateLinks |
boolean |
required |
communityPass
POST /api/community/pass
A one-time address back into a listed project’s room, signed in, to comment or react. Any signed-in person may ask, for any listed project; the room learns their public face and nothing else. A project that is not listed is 404.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
slug |
string |
required · 1–63 characters |
Reply: object
| Field | Type | |
|---|---|---|
url |
string (uri) |
required |
Arranging projects
Your workspace’s own order and groups of projects, as the app’s side bar and Projects page draw them, and where each project stands on the community. Cosmetic: nothing here changes who can see a project.
getArrangement
GET /api/arrangement
Your workspace’s groups in order, and every project once, in the order it is drawn: the ungrouped first, then each group’s. A project nobody has placed yet comes first, newest first. Each says where it stands on the community: unlisted, listed, blocked (a check kept it off), pulled (taken off) or pending (waiting for review). Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
groups |
object[] |
required |
projects |
object[] |
required |
arrangeProjects
POST /api/arrangement
Sets the order: the ungrouped projects, then each group with its projects, groups in the order given. What is left out keeps its place after what was given. Each id at most once (422 otherwise); a project or group not your workspace’s is 404, and nothing moves.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
ungrouped |
string[] |
required · at most 500 items |
groups |
object[] |
required · at most 30 items |
Reply: object
| Field | Type | |
|---|---|---|
groups |
object[] |
required |
projects |
object[] |
required |
createGroup
POST /api/groups
Adds an empty group, last. Its name is 1 to 40 characters; a workspace has at most 30 groups.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
name |
string |
required · 1–40 characters |
Reply: object
| Field | Type | |
|---|---|---|
groups |
object[] |
required |
projects |
object[] |
required |
renameGroup
POST /api/groups/:groupId
Renames one group. Another workspace’s group is 404.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: groupId
Body: object
| Field | Type | |
|---|---|---|
name |
string |
required · 1–40 characters |
Reply: object
| Field | Type | |
|---|---|---|
groups |
object[] |
required |
projects |
object[] |
required |
deleteGroup
POST /api/groups/:groupId/delete
Deletes one group and nothing else: its projects move to the end of the ungrouped. Another workspace’s group is 404.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: groupId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
groups |
object[] |
required |
projects |
object[] |
required |
Public
Answers anyone, signed in or not, and shows only what is listed on the community.
profile
GET /api/community/people/:handle
A publisher’s public page, by handle: their name, avatar and bio, and their projects listed on the community. A handle nobody has is 404.
Open to anyone, signed in or not.
Path parameters: handle
Body: none
Reply: object
| Field | Type | |
|---|---|---|
handle |
string |
required |
name |
string |
required |
bio |
string | null |
required |
image |
string (uri) | null |
required |
projects |
object[] |
required |
listings
GET /api/community/listings
Every project listed on the community, newest listing first: each card’s title and line, its public address, and whether it has a cover (at /api/community/projects/:slug/cover). Nothing about who built it: no name, handle, avatar or email. Cached for a minute; getroomi.com may read it from a browser.
Open to anyone, signed in or not.
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
slug |
string |
required |
title |
string |
required |
line |
string |
required |
url |
string (uri) |
required |
cover |
boolean |
required |
listedAt |
string (date-time) |
required |
credit |
string | null |
optional |
Plan and usage
The workspace’s plan, and what it has used this month.
getBilling
GET /api/billing
Your workspace’s plan and its status, this month’s usage, the viewer-hours allowance and where it stands, and whether the workspace is suspended. Paid plans cannot be bought yet. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
plan |
"free" | "pro" | "team" | "organisation" | "solo" | "studio" | "agency" | "enterprise" |
required |
kind |
"personal" | "team" |
optional |
seats |
object |
optional |
interval |
"month" | "year" | null |
required |
status |
"active" | "past_due" | "canceled" |
required |
currentPeriodEnd |
string (date-time) | null |
required |
usage |
object |
required |
allowance |
object |
required |
history |
object |
optional |
suspended |
boolean |
required |
paidPlans |
"coming-soon" |
required |
projectBounces
GET /api/projects/:projectId/bounces
How many viewers a full room turned away from the project this calendar month (UTC), counted once per session per hour. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: projectId
Body: none
Reply: object
| Field | Type | |
|---|---|---|
thisMonth |
integer |
required |
You, and your workspace’s people
Your name and photo as the app draws you, how you sign in and where you are signed in, deleting your account, and your workspace’s people and container demos. Nothing here names anyone else to change.
whoami
GET /api/me/account
Who you are, as the app draws you: your name (or your email while you have set none), your picture — your uploaded photo, else your sign-in provider’s avatar, else none — and the workspace your session is in, with your role. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
userId |
string |
required · not empty |
name |
string |
required |
displayName |
string |
required |
nameChosen |
boolean |
optional |
suggestedName |
string |
optional |
email |
string |
required |
emailVerified |
boolean |
required |
image |
string | null |
required |
photo |
object | null |
required |
providerImage |
boolean |
required |
handle |
string | null |
required |
workspace |
object |
required |
createdAt |
string (date-time) |
required |
setName
POST /api/me/name
Sets the name you are shown by, to you and to the people in your workspace: 1 to 80 characters.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
name |
string |
required · 1–80 characters |
Reply: object
| Field | Type | |
|---|---|---|
userId |
string |
required · not empty |
name |
string |
required |
displayName |
string |
required |
nameChosen |
boolean |
optional |
suggestedName |
string |
optional |
email |
string |
required |
emailVerified |
boolean |
required |
image |
string | null |
required |
photo |
object | null |
required |
providerImage |
boolean |
required |
handle |
string | null |
required |
workspace |
object |
required |
createdAt |
string (date-time) |
required |
removePhoto
POST /api/me/photo/remove
Removes your uploaded photo; you are drawn with your provider’s avatar, or your initials. A photo is uploaded with PUT /api/me/photo: a PNG, JPEG or WebP image of up to 2 MB and 4096 pixels a side, checked by its own bytes.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
userId |
string |
required · not empty |
name |
string |
required |
displayName |
string |
required |
nameChosen |
boolean |
optional |
suggestedName |
string |
optional |
email |
string |
required |
emailVerified |
boolean |
required |
image |
string | null |
required |
photo |
object | null |
required |
providerImage |
boolean |
required |
handle |
string | null |
required |
workspace |
object |
required |
createdAt |
string (date-time) |
required |
deletable
GET /api/me/deletable
Whether your account can be deleted now, and what stands in the way: a workspace you own that still holds projects or other people. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
allowed |
boolean |
required |
blockers |
object[] |
required |
deleteAccount
POST /api/me/delete
Deletes your account: your sign-in methods, passkeys and sessions, your profile and photo, and every workspace you own. confirm must be your email. Refused with 422 while deletable names a workspace.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
confirm |
string |
required |
Reply: object
| Field | Type | |
|---|---|---|
deleted |
true |
required |
containerUsage
GET /api/usage/containers
Your workspace’s container demos this calendar month (UTC): runs, container-hours, starts that failed, viewers held back at the plan’s cap, and the median start. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
month |
string |
required |
runs |
integer |
required |
containerHours |
number |
required |
failedStarts |
integer |
required |
heldBack |
integer |
required |
medianColdStartMs |
integer | null |
required |
members
GET /api/workspace/people
The people in your workspace, each with the name and picture the app draws them with. Their photos are served at GET /api/people/:userId/photo to people in a workspace with them, and to nobody else. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
userId |
string |
required · not empty |
displayName |
string |
required |
image |
string | null |
required |
signIns
GET /api/me/sign-in
How you can sign in: GitHub and Google, each set up on the platform or not and linked to you or not, how many passkeys you have, and whether emailed links are offered. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
providers |
object[] |
required |
passkeys |
integer |
required |
magicLinks |
boolean |
required |
disconnect
POST /api/me/sign-in/:provider/disconnect
Unlinks GitHub or Google from your account. Refused with 422 while it is your only way in: no other provider, and no passkey.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: provider
Body: none
Reply: object
| Field | Type | |
|---|---|---|
providers |
object[] |
required |
passkeys |
integer |
required |
magicLinks |
boolean |
required |
sessions
GET /api/me/sessions
Every live session of yours, newest first, with the one asking marked current. Each has an id to sign it out by, never the token a cookie carries. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
current |
boolean |
required |
signInMethod |
string | null |
required |
userAgent |
string | null |
required |
device |
string |
required |
createdAt |
string (date-time) |
required |
expiresAt |
string (date-time) |
required |
revokeSession
POST /api/me/sessions/:sessionId/revoke
Signs out one of your sessions. Another person’s session is a 404, and nothing changes.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Path parameters: sessionId
Body: none
Reply: object[]
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
current |
boolean |
required |
signInMethod |
string | null |
required |
userAgent |
string | null |
required |
device |
string |
required |
createdAt |
string (date-time) |
required |
expiresAt |
string (date-time) |
required |
signOutEverywhere
POST /api/me/sessions/revoke-all
Signs out every session of yours, the one asking included.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
signedOut |
integer |
required |
Your first days
The welcome letter, whether you have read it, and a note to the Roomi team from inside the app. Each answers only for the person signed in.
myPreferences
GET /api/me/preferences
Your own preferences: whether you have read the welcome letter and whether the feedback tab is folded. Someone with none yet gets the defaults. Changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
welcomeSeenAt |
string (date-time) | null |
required |
feedbackTabFolded |
boolean |
required |
welcomeSeen
POST /api/me/welcome/seen
Marks the welcome letter as read, the first time only; calling it again changes nothing.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: none
Reply: object
| Field | Type | |
|---|---|---|
welcomeSeenAt |
string (date-time) | null |
required |
feedbackTabFolded |
boolean |
required |
foldFeedbackTab
POST /api/me/feedback-tab
Folds the “Help us improve” button to a small tab, or opens it out again. Yours alone.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
folded |
boolean |
required |
Reply: object
| Field | Type | |
|---|---|---|
welcomeSeenAt |
string (date-time) | null |
required |
feedbackTabFolded |
boolean |
required |
sendProductFeedback
POST /api/product-feedback
A note to the Roomi team, filed under you and the workspace you are signed in to, whatever the body says. Up to 2,000 characters, and a few an hour; past that, 429. Says back only that it arrived.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
body |
string |
required · 1–2000 characters |
page |
string |
optional · at most 300 characters |
appVersion |
string |
optional · at most 64 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |
createdAt |
string (date-time) |
required |
Reports
Reporting a project to Roomi.
reportProject
POST /api/reports
Reports a project, by its address, to Roomi, with a reason and any detail. A project that does not exist is 404; more than 20 reports from one person in 24 hours is 429.
Signed in: a session cookie (the app) or Authorization: Bearer <token> (the CLI and the connector).
Body: object
| Field | Type | |
|---|---|---|
slug |
string |
required · 1–63 characters |
reason |
"phishing" | "impersonation" | "abuse" | "illegal" | "spam" | "other" |
required |
detail |
string |
optional · at most 1000 characters |
Reply: object
| Field | Type | |
|---|---|---|
id |
string |
required · not empty |