Channels
A channel is anything that holds messages. Four types share a single endpoint surface; type-specific fields ride alongside the common ones.
| Type | Value | Created by | Notes |
|---|---|---|---|
| DM | 0 | POST /v1/users/@me/channels with { recipient_id } | Two-recipient. Idempotent — opening with the same recipient returns the existing channel. |
| GROUP_DM | 1 | POST /v1/users/@me/channels with { recipient_ids } (2–9) | Multi-recipient. New row per call. |
| HUB | 2 | POST /v1/channels with { type: 2, name, ... } | Spatial; carries a scene. |
| HOME | 3 | Auto-created at signup. Never creatable via API. | One per user, immutable in count. |
The Channel object
- Name
id- Type
- string
- Description
Snowflake.
- Name
type- Type
- ChannelType
- Description
See table above.
- Name
name- Type
- string | null
- Description
Optional for DMs and group DMs; required (1–100 chars) for HUB and HOME.
- Name
topic- Type
- string | null
- Description
Up to 1024 chars. HUB / HOME only — DMs ignore it.
- Name
icon_hash- Type
- string | null
- Description
sha256[0:16] content hash on the same
CDN_BUCKETas avatars.
- Name
last_message_id- Type
- string | null
- Description
Snowflake of the most recent message. Used for sort + unread tracking.
- Name
scene- Type
- unknown | null
- Description
Spatial scene JSON. HUB / HOME only. Writing this field requires
channels.write_scene— the wormhole game server is the normal caller. Other fields on the same PATCH stay open to regular users.
- Name
owner_id- Type
- string | null
- Description
HUB / HOME owner. Null for DM / GROUP_DM and for HUB / HOME whose original owner was deleted.
- Name
created_at- Type
- string
- Description
- Name
updated_at- Type
- string
- Description
Hydrated form
GET /v1/channels/:channelId returns a ChannelWithRecipients — the same shape plus a recipients: PublicUser[] array of the other participants. The requesting user is omitted; the client doesn't have to filter itself out of every DM. HUB / HOME channels return an empty array since they don't model recipients individually.
Open a DM
Single recipient. Idempotent — calling twice with the same recipient_id returns the same channel row both times.
Request
curl -X POST https://api.localuniverse.io/v1/users/@me/channels \
-H "authorization: <token>" \
-H "content-type: application/json" \
-d '{ "recipient_id": "312323..." }'
Open a group DM
Two to nine recipients. Each call creates a new channel row — group DMs are not deduplicated by recipient set.
- Name
recipient_ids- Type
- string[]
- Description
2–9 user ids.
- Name
name- Type
- ?string
- Description
Optional. 1–100 chars. Defaults to a comma-joined display-name string if omitted.
Request
curl -X POST https://api.localuniverse.io/v1/users/@me/channels \
-H "authorization: <token>" \
-H "content-type: application/json" \
-d '{
"recipient_ids": ["312323...", "412412..."],
"name": "Project hangout"
}'
Create a HUB
Only HUBs are creatable through POST /v1/channels. The Zod schema literally rejects type: 3 so HOMEs can't be created here — the only HOME a user gets is the one auto-provisioned at signup.
- Name
type- Type
- 2
- Description
Literal — HUB only.
- Name
name- Type
- string
- Description
1–100 chars, required.
- Name
topic- Type
- ?string
- Description
Up to 1024 chars.
- Name
icon_hash- Type
- ?string
- Description
Optional sha256[0:16] hash.
Request
curl -X POST https://api.localuniverse.io/v1/channels \
-H "authorization: <token>" \
-H "content-type: application/json" \
-d '{
"type": 2,
"name": "Backyard arcade",
"topic": "drop in any time"
}'
Read a channel
Returns the full ChannelWithRecipients. 404 if the caller can't see the channel — the API never distinguishes "doesn't exist" from "you aren't a member" to avoid leaking existence.
Request
curl https://api.localuniverse.io/v1/channels/3123... \
-H "authorization: <token>"
Update a channel
PATCH semantics: omit = leave, value = set, null = clear.
Writing scene requires channels.write_scene — every other field is open to channel participants. The other fields validate in one schema; permission for each is checked per-field inside the service.
- Name
name- Type
- ?string
- Description
1–100 chars;
nullclears (DMs only).
- Name
topic- Type
- ?string
- Description
Up to 1024 chars.
- Name
icon_hash- Type
- ?string
- Description
- Name
scene- Type
- unknown
- Description
Spatial scene JSON. HUB / HOME only.
Request
curl -X PATCH https://api.localuniverse.io/v1/channels/3123... \
-H "authorization: <token>" \
-H "content-type: application/json" \
-d '{ "topic": "moved to weekly" }'
Delete a channel
Deletes the channel and cascades its messages. Permission requirements depend on type — DMs need owner consent on both sides (you can only DELETE your own copy), group DMs require any participant.
Request
curl -X DELETE https://api.localuniverse.io/v1/channels/3123... \
-H "authorization: <token>"
Recipients
| Method | Path | Notes |
|---|---|---|
DELETE | /v1/channels/:channelId/recipients/@me | Leave a group DM. The literal /@me is matched before /:userId. |
PUT | /v1/channels/:channelId/recipients/:userId | Add a user to a group DM. GROUP_DM only — DMs reject. |
Messages
Live under /v1/channels/:channelId/messages — see Messages for the full surface, inline-token format, and pagination.