purrr for developers
purrr is a self-hosted, open chat platform. Everything the official client does goes through the same public REST API and WebSocket gateway documented here — there are no private endpoints.
Bots are live. Create one in the app under Settings → Bots, copy its token, and jump to the bot section.
Base URL
https://purrr.chat/api
Authentication
Log in to get a bearer token, then send it on every request:
curl -s https://purrr.chat/api/auth/login \
-H 'content-type: application/json' \
-d '{"login": "your-username", "password": "…"}'
# → {"access_token": "…", "refresh_token": "…", "user": {…}}
curl -s https://purrr.chat/api/users/@me \
-H 'authorization: Bearer ACCESS_TOKEN'
Access tokens are short-lived; exchange the refresh token at
POST /api/auth/refresh when they expire.
Core endpoints
| Endpoint | What it does | |
|---|---|---|
| get | /api/guilds/@me | Servers you're in (incl. your role ids) |
| get | /api/guilds/{id} | Server detail: channels, roles |
| get | /api/channels/{id}/messages | Message history (snowflake-paginated) |
| post | /api/channels/{id}/messages | Send a message |
| post | /api/users/@me/channels | Open a DM |
| get | /api/users/{id} | Public profile (incl. connections) |
| post | /api/attachments | Upload a file (multipart) |
Ids are snowflakes, serialized as strings in JSON. Mentions use
<@id> / <@&roleId>, custom emoji <:name:id>;
a message starting with @silent pings nobody.
Bots
A bot is a real account your code drives — same REST API, same gateway, same permission system (give it roles like any member). Differences from a person:
- It authenticates with a static token instead of a password:
Authorization: Bot <token>(no login/refresh dance, no expiry). - It joins servers when someone with Manage Server adds it — in the app
(Settings → Bots → “Add to server”) or via
POST /api/guilds/{id}/bots {"bot_id": "…"}. - It wears a BOT badge everywhere.
Manage bots with your own (person) token:
| Endpoint | What it does | |
|---|---|---|
| post | /api/bots | Create a bot ({"username": "…"}) — returns the token once |
| get | /api/bots | List your bots |
| post | /api/bots/{id}/token | Rotate the token (old one dies instantly) |
| post | /api/guilds/{id}/bots | Add a bot to a server you manage |
A minimal echo bot is a WebSocket connection and one POST:
// identify on the gateway with the bot token:
→ {"op": "identify", "d": {"token": "Bot 1234.abc…"}}
← {"t": "MESSAGE_CREATE", "d": {"channel_id": "…", "content": "!ping", …}}
// then answer over REST:
POST /api/channels/{channel_id}/messages
Authorization: Bot 1234.abc…
{"content": "pong 🐾"}
Realtime gateway
Connect to wss://purrr.chat/gateway/ws, then authenticate with an identify frame
(the token never rides in the URL):
→ {"op": "identify", "d": {"token": "ACCESS_TOKEN"}}
← {"op": "hello", "d": {"heartbeat_interval": 30000}}
→ {"op": "heartbeat"} // every interval
← {"t": "READY", "d": {…}} // initial state
← {"t": "MESSAGE_CREATE", "d": {…}} // events as they happen
Event payloads mirror the REST resources: MESSAGE_CREATE,
MESSAGE_UPDATE, MESSAGE_DELETE, TYPING,
PRESENCE_UPDATE, VOICE_STATE_UPDATE, and friends.
Rules of the road
- Rate limits are generous but real — back off on
429. - Registration on purrr.chat is currently open; self-hosting instructions live in the repo.
- Be kind. It's a small instance and the admins read the logs.