Channels

A channel is anything that holds messages. Four types share a single endpoint surface; type-specific fields ride alongside the common ones.

TypeValueCreated byNotes
DM0POST /v1/users/@me/channels with { recipient_id }Two-recipient. Idempotent — opening with the same recipient returns the existing channel.
GROUP_DM1POST /v1/users/@me/channels with { recipient_ids } (2–9)Multi-recipient. New row per call.
HUB2POST /v1/channels with { type: 2, name, ... }Spatial; carries a scene.
HOME3Auto-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_BUCKET as 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

POST
/v1/users/@me/channels
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

POST
/v1/users/@me/channels
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

POST
/v1/channels
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

GET
/v1/channels/:channelId
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; null clears (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

PATCH
/v1/channels/:channelId
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

DELETE
/v1/channels/:channelId
curl -X DELETE https://api.localuniverse.io/v1/channels/3123... \
  -H "authorization: <token>"

Recipients

MethodPathNotes
DELETE/v1/channels/:channelId/recipients/@meLeave a group DM. The literal /@me is matched before /:userId.
PUT/v1/channels/:channelId/recipients/:userIdAdd 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.

Was this page helpful?