zwip.chat API
Create ephemeral rooms and pipe your own apps into them. The headline feature is incoming webhooks — post a line of JSON and it appears live in a room, which makes zwip a dead-simple logging, alerting, and notification sink.
Overview
All endpoints accept and return JSON unless noted. There is no account system: access is granted by three secrets.
- Room code — the 7-character code (e.g.
X7K9M2P) that identifies a room. Anyone with the code can read it, post to it, and manage its webhooks. - Webhook token — a secret minted per room that lets an external app post messages without a browser session.
- Admin token — returned once when a room is created; the only authority that can destroy the room. Store it if you'll need to delete the room programmatically — it is never shown again.
Base URL in the examples below is https://zwip.chat — replace it with your own host if self-hosting.
410 Gone or 404.ZWIP_ENC_KEY environment variable (see DEPLOYMENT.md).Quick start
Create a room, mint a webhook, and post to it — three requests:
# 1. Create a room -> returns its code curl -X POST https://zwip.chat/rooms # { "code": "X7K9M2P" } # 2. Create an incoming webhook for that room -> returns a one-time token + URL curl -X POST https://zwip.chat/rooms/X7K9M2P/webhooks \ -H "Content-Type: application/json" \ -d '{"label": "Deploy Bot"}' # { "id": 1, "label": "Deploy Bot", "token": "kQ2f…", "url": "https://zwip.chat/hooks/in/X7K9M2P/kQ2f…" } # 3. Post a message through the webhook URL curl -X POST https://zwip.chat/hooks/in/X7K9M2P/kQ2f… \ -H "Content-Type: application/json" \ -d '{"content": "✅ Build #1423 passed"}' # { "ok": true, "message_id": 42 }
Open https://zwip.chat/chat/X7K9M2P in a browser and you'll see webhook messages arrive in real time.
Rooms › Create a room
Creates a new empty room and returns its code and a one-time admin token. Takes no body.
curl -X POST https://zwip.chat/rooms 200 OK { "code": "X7K9M2P", "admin_token": "7vSzWASv…" }
admin_token is shown only here, once. It is the sole authority to destroy the room (see below). Save it if you need programmatic delete; it cannot be recovered later.Rooms › Get a room
Returns room metadata, or 404 if the room does not exist or has expired.
curl https://zwip.chat/rooms/X7K9M2P 200 OK { "id": 12, "code": "X7K9M2P", "name": "Launch sync", "created_at": "2026-06-30T18:24:03", "last_active": "2026-06-30T18:31:10", "is_active": 1 }
Rooms › Rename a room
Sets the room's display name. Send it as a form field named name (not JSON).
| Field | Type | Required | Description |
|---|---|---|---|
name | string (form) | Yes | New display name, up to 50 characters. |
curl -X POST https://zwip.chat/rooms/X7K9M2P/rename \ -d "name=Launch sync" 200 OK { "status": "ok" }
Rooms › Destroy a room
Runs a slash command against the room — the same actions a user can type in the message box. Pass the command as JSON; the leading slash is optional. Admin commands require the room's admin_token in the X-Admin-Token header.
The only command today is /destroy (alias /close), which permanently deletes the room, its messages and its uploaded files, and disconnects everyone. Live clients receive a destroyed event over the WebSocket.
| Field | Type | Required | Description |
|---|---|---|---|
command | string (JSON) | Yes | e.g. /destroy or destroy. |
curl -X POST https://zwip.chat/rooms/X7K9M2P/commands \ -H "Content-Type: application/json" \ -H "X-Admin-Token: 7vSzWASv…" \ -d '{"command": "/destroy"}' 200 OK { "status": "destroyed", "command": "destroy" }
| Status | Meaning |
|---|---|
403 | Missing or wrong admin token for an admin command. |
400 | Unknown command. |
404 | The room does not exist or has expired. |
There is also a lower-level REST form: DELETE /rooms/{code} with the same X-Admin-Token header. The command endpoint is preferred because it's the shared path for every slash command.
Rooms › Read messages
Returns the room's messages in chronological order (up to the 100 most recent). Handy for archiving a room before it expires.
curl https://zwip.chat/rooms/X7K9M2P/messages 200 OK [ { "id": 42, "room_code": "X7K9M2P", "sender": "Deploy Bot", "content": "✅ Build #1423 passed", "file_path": null, "file_type": null, "created_at": "2026-06-30T18:31:10" } ]
Incoming webhooks › Create a webhook
Mints an incoming webhook for the room and returns a secret token plus the full URL to post to. The token is shown only once — store it now, it can't be retrieved later.
| Field | Type | Required | Description |
|---|---|---|---|
label | string | No | A human name for the integration (e.g. "Deploy Bot"). Used as the default message sender. |
curl -X POST https://zwip.chat/rooms/X7K9M2P/webhooks \ -H "Content-Type: application/json" \ -d '{"label": "Deploy Bot"}' 200 OK { "id": 1, "room_code": "X7K9M2P", "label": "Deploy Bot", "token": "kQ2f8s…one-time-secret…", "url": "https://zwip.chat/hooks/in/X7K9M2P/kQ2f8s…" }
Incoming webhooks › List webhooks
Lists the room's active webhooks. Tokens are never returned — only metadata.
curl https://zwip.chat/rooms/X7K9M2P/webhooks 200 OK [ { "id": 1, "room_code": "X7K9M2P", "label": "Deploy Bot", "created_at": "2026-06-30T18:25:00" } ]
Incoming webhooks › Revoke a webhook
Permanently deactivates a webhook. Its token stops working immediately. Returns 404 if no matching active webhook exists.
curl -X DELETE https://zwip.chat/rooms/X7K9M2P/webhooks/1 200 OK { "status": "revoked" }
Incoming webhooks › Post a message
Posts a message into the room as the webhook. The message is stored and broadcast to everyone currently viewing the room in real time.
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | The message text. Must not be empty. |
sender | string | No | Display name for this message. Defaults to the webhook's label, or "webhook". |
curl -X POST https://zwip.chat/hooks/in/X7K9M2P/kQ2f8s… \ -H "Content-Type: application/json" \ -d '{"sender": "Alertmanager", "content": "🔥 CPU > 90% on web-03"}' 200 OK { "ok": true, "message_id": 43 }
Error responses
| Status | Meaning |
|---|---|
401 | Unknown or revoked token, or the token doesn't belong to this room. |
410 | The room has expired or been closed. |
422 | content was missing or empty. |
Incoming webhooks › Security & limits
- Tokens are secrets. Only a SHA-256 hash is stored server-side; the plaintext is returned once at creation and is unrecoverable. Treat it like an API key and keep it out of client-side code and version control.
- Scope. A token can only post to the single room it was minted for, and only create messages — it cannot read history, rename, or manage other webhooks.
- Revoke freely. Each room can hold many webhooks; revoke one without affecting the others.
- Room-code trust model. In this release, anyone holding a room code can create or revoke that room's webhooks — the same as anyone with the code being able to post. A per-room admin model is on the roadmap.
- Ephemerality. When a room expires, its webhooks stop working. Archive via Read messages before that if you need a durable record.
Claude skill
zwip ships a Claude Code skill so you can drive rooms straight from Claude — "post this to my zwip room", "create a webhook", "read the room". It wraps the same API documented above in a small, dependency-free CLI, and lives in the repo at .claude/skills/zwip/.
Once the skill is available to your Claude session, just ask in plain language. Under the hood it runs:
# create a room -> code + chat URL python3 .claude/skills/zwip/zwip.py create-room --name "Deploy log" # post a message to a room you have the code for (no token needed) python3 .claude/skills/zwip/zwip.py send --room X7K9M2P --content "Build passed" --sender "CI" # attach a file python3 .claude/skills/zwip/zwip.py send --room X7K9M2P --file ./build.log # create + post via an incoming webhook (for repeated automation) python3 .claude/skills/zwip/zwip.py new-webhook --room X7K9M2P --label "Deploy Bot" python3 .claude/skills/zwip/zwip.py webhook-send --url "…/hooks/in/X7K9M2P/TOKEN" --content "hi" # read a room's messages python3 .claude/skills/zwip/zwip.py read --room X7K9M2P # destroy a room (needs the admin_token from create-room) python3 .claude/skills/zwip/zwip.py destroy --room X7K9M2P --token "7vSzWASv…"
Set ZWIP_BASE (or pass --base) to point the skill at a self-hosted instance. Full details are in SKILL.md in the repo.
Slash commands Roadmap
The first slash command is live: /destroy (see Destroy a room). The rest below are planned — a preview of the intended surface, with the full backlog in SLASH_COMMANDS.md. They let users and room admins run actions by typing /command in the message box, à la Slack, Discord, and Telegram.
| Command | Who | Status | Description |
|---|---|---|---|
/destroy (alias /close) | Admin | Available | Permanently delete the room and everything in it. |
/help | Anyone | Roadmap | List available commands with inline help. |
/rename <name> | Admin | Roadmap | Rename the room (API equivalent exists today). |
/invite | Anyone | Roadmap | Show and copy the room's invite link and code. |
/who | Anyone | Roadmap | List everyone currently in the room. |
/me <action> | Anyone | Roadmap | Post a third-person action message. |
/code [lang] | Anyone | Roadmap | Format the message as a code block. |
/kick @user | Admin | Roadmap | Remove a participant from the room. |
/ban @user | Admin | Roadmap | Remove and block a participant from rejoining. |
/mute @user [time] | Admin | Roadmap | Temporarily prevent a participant from posting. |
/slowmode <secs> | Admin | Roadmap | Rate-limit how often participants can post. |
/kick, /ban, /mute) depend on a room admin/permission model and stable per-session identity, both of which land before those commands ship.