Skip to content
esc

Search pages and sections. Try a tool name, an error code, a route.

↑↓ navigate↵ openesc close

API reference · Data plane

Spaces

A space is a group of end users inside one app — a household, a team, a couple. Rows on the espaces and espace_modere presets belong to a space, and only its members reach them.

Host: https://proj-<id>.manybases.com · Key: pk_ with X-Session (an end user), or sk_. The same routes exist for an account key under /v1/projects/:pid/spaces/….

POST/v1/spaces { name }
Whoa signed-in user
EffectCreates the space; the creator is its admin. 201 { data: { id, name, role: "admin", created_at } }
GET/v1/spaces
Whoa signed-in user, sk_, ak_
EffectA user sees their own spaces with their role ({ id, name, role, created_at }); a key sees every space of the project, without role
GET/v1/spaces/:id/members
Whomembers, sk_, ak_
Effect{ user_id, role } per member. Does not paginate
POST/v1/spaces/:id/members { user_id }
Whothe space admin, sk_, ak_
EffectAdds a member — always as member; a role in the body is refused with 400. Already a member → 409 conflict
DELETE/v1/spaces/:id/members/:user_id
Whothe space admin, sk_, ak_ — or a member leaving
EffectRemoves a member. The admin can never be removed, not even by themselves → 403 with the two ways out
PATCH/v1/spaces/:id { name?, admin? }
Whothe space admin, sk_, ak_
EffectRenames, or transfers the admin role to an existing member
DELETE/v1/spaces/:id
Whothe space admin
EffectDeletes the space and every row that belongs to it

Lifecycle needs a user#

Creating and deleting a space require an end-user session: a space is born from a user, who becomes its admin, and dies by its admin. sk_ and ak_ receive 401 on these two calls. They manage everything else — listing, members, renaming, transferring the admin role. This is a convention that expresses the product's intent, not a security boundary: a server key that wants to bypass it can.

Six invariants#

  1. A living space has exactly one active admin.
  2. POST /v1/spaces/:id/members always adds a member. Nobody is born admin; the role is transferred.
  3. The target of a transfer is already a member.
  4. An unknown user_id and a non-member answer the same 404 — otherwise the route would enumerate the app's users.
  5. Membership is a set: a user appears at most once, and re-adding an existing member answers 409 conflict.
  6. The admin is never removed from the member list, not even by themselves: 403 forbidden, and the hint names the two exits — transfer the role if other members remain, or delete the space if they are alone.

Joining a space consumes no seat: the member is already an end user of the app, and their seat was taken when they signed up.

Four gestures confirm in two steps#

Deleting a space, adding a member, removing a member and transferring the admin role each answer 428 confirmation_required on the first call, with a summary (members removed, rows destroyed, whether it is reversible) and a confirmation_token; the second call with the token executes. This applies to everyone, the human admin included — it is the admin who erases every member's rows in one call. No signed-in human of the account is required: requiring one would make spaces unusable from an agent.

The "already a member" check runs before any token is issued: no token is ever emitted for a gesture that would be refused anyway.

What happens to rows#

  • A deleted space takes its rows with it, on every spaces preset.
  • A row's space_id is immutable. To move a row, delete and recreate it.
  • Switching a table out of a spaces preset resets the space_id of its rows to null. Switching a non-empty table into a spaces preset is refused with 409; switching between the two spaces presets always passes.
  • A row is visible to the members of its space. On espace_modere, only the row's author or the space admin can edit or delete it.

An identity that is not an end user — an sk_ or ak_ writing rows — has no spaces of its own: it administers memberships but never becomes a member, and the space and space_admin cells of the presets evaluate to nobody for it. It still reads and writes every row through its own rights.