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.

Base URL in the examples below is https://zwip.chat — replace it with your own host if self-hosting.

timer
Rooms are ephemeral: they expire after 60 minutes of inactivity. Posting a message (including via a webhook) counts as activity and keeps the room alive. A request to an expired room returns 410 Gone or 404.
enhanced_encryption
Message content, sender names, room names and uploaded files are encrypted at rest (AES-256-GCM) — nothing is stored in plaintext. This is transparent to the API: you send and receive plaintext, and the server encrypts on write and decrypts on read. It is not end-to-end encryption. Self-hosters set the key via the 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

POST/rooms

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…" }
key
The 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

GET/rooms/{code}

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

POST/rooms/{code}/rename

Sets the room's display name. Send it as a form field named name (not JSON).

FieldTypeRequiredDescription
namestring (form)YesNew 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

POST/rooms/{code}/commands

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.

FieldTypeRequiredDescription
commandstring (JSON)Yese.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" }
StatusMeaning
403Missing or wrong admin token for an admin command.
400Unknown command.
404The 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

GET/rooms/{code}/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

POST/rooms/{code}/webhooks

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.

FieldTypeRequiredDescription
labelstringNoA 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

GET/rooms/{code}/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

DELETE/rooms/{code}/webhooks/{id}

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

POST/hooks/in/{code}/{token}

Posts a message into the room as the webhook. The message is stored and broadcast to everyone currently viewing the room in real time.

FieldTypeRequiredDescription
contentstringYesThe message text. Must not be empty.
senderstringNoDisplay 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

StatusMeaning
401Unknown or revoked token, or the token doesn't belong to this room.
410The room has expired or been closed.
422content was missing or empty.

Incoming webhooks  ›  Security & limits

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.

CommandWhoStatusDescription
/destroy (alias /close)AdminAvailablePermanently delete the room and everything in it.
/helpAnyoneRoadmapList available commands with inline help.
/rename <name>AdminRoadmapRename the room (API equivalent exists today).
/inviteAnyoneRoadmapShow and copy the room's invite link and code.
/whoAnyoneRoadmapList everyone currently in the room.
/me <action>AnyoneRoadmapPost a third-person action message.
/code [lang]AnyoneRoadmapFormat the message as a code block.
/kick @userAdminRoadmapRemove a participant from the room.
/ban @userAdminRoadmapRemove and block a participant from rejoining.
/mute @user [time]AdminRoadmapTemporarily prevent a participant from posting.
/slowmode <secs>AdminRoadmapRate-limit how often participants can post.
construction
Moderation commands (/kick, /ban, /mute) depend on a room admin/permission model and stable per-session identity, both of which land before those commands ship.