API reference¶
chatwire's web server exposes a REST API, an SSE stream for real-time updates, and an MCP stdio server for LLM agent access.
Base URL¶
Default: http://localhost:8723
Configure port in ~/.chatwire/config.json:
Authentication¶
When no password is set, all endpoints are open. When a password is set:
- Browser sessions — cookie-based (set by
/login); 30-day sliding expiry - API clients — Bearer token:
Authorization: Bearer cwk_<hex>
Generate API keys from Settings → Security → API Keys.
Scopes:
- read — read conversations, messages, contacts
- write — send messages
Endpoints requiring write scope are noted below.
Health¶
GET /healthz¶
Basic alive check. Returns 200 when the web server is running.
GET /api/heartbeat¶
Detailed health including bridge liveness.
{
"ok": true,
"ts": 1715900000.0,
"bridge_alive": true,
"last_heartbeat": 1715899998.0,
"last_message_ts": "2026-05-16T14:30:00Z"
}
| Field | Type | Description |
|---|---|---|
ok |
bool | Always true when the web server responds |
ts |
float | Current server unix timestamp |
bridge_alive |
bool | True if bridge heartbeat is < 10s old |
last_heartbeat |
float or null | Unix timestamp of last bridge poll |
last_message_ts |
string or null | Timestamp of most recent message |
GET /version¶
Conversations¶
GET /api/v1/conversations¶
List recent conversations.
Query params:
- limit (int, default 50) — max conversations to return
- offset (int, default 0) — pagination offset
Response:
[
{
"guid": "iMessage;-;+15551234567",
"handle": "+15551234567",
"display_name": "Alice",
"last_message": "Are you around?",
"last_message_ts": "2026-05-16T14:00:00Z",
"unread_count": 2,
"is_group": false
}
]
GET /api/v1/conversations/{guid}/messages¶
Get messages in a conversation.
Path params:
- guid — chat GUID (URL-encoded)
Query params:
- limit (int, default 100) — messages to return
- before_rowid (int) — paginate backwards from this ROWID
Response:
[
{
"rowid": 12345,
"guid": "message-guid",
"text": "Are you around?",
"sender": "Alice",
"handle": "+15551234567",
"is_from_me": false,
"ts": "2026-05-16T14:00:00Z",
"read_at": null,
"tapbacks": ["❤️"],
"reply_to_guid": null,
"edited": false,
"attachments": []
}
]
Sending¶
POST /api/v1/send (requires write scope)¶
Send a message to a handle.
Request body:
Response:
POST /api/v1/send/chat (requires write scope)¶
Send a message to a chat GUID (for group chats).
POST /api/v1/send/file (requires write scope)¶
Upload and send an attachment. Multipart form data:
- handle — recipient handle or chat_guid
- file — the file bytes
Settings¶
GET /api/v1/settings¶
Get all settings.
Response: the full config dict (password hash excluded).
POST /api/v1/settings¶
Update settings. Sends the config fields to update (partial update supported).
GET /api/v1/whitelist¶
List whitelisted handles.
POST /api/v1/whitelist/add¶
Add a handle to the whitelist.
POST /api/v1/whitelist/remove¶
Remove a handle from the whitelist.
Plugins¶
GET /api/v1/plugins¶
List discovered plugins with their enable state and schema.
[
{
"name": "ntfy",
"display_name": "ntfy Notifications",
"description": "Push notifications via ntfy.sh",
"enabled": true,
"tier": "notify",
"settings_schema": { ... }
}
]
POST /api/v1/plugins/{name}/enable¶
Enable a plugin.
POST /api/v1/plugins/{name}/disable¶
Disable a plugin.
Automation rules¶
GET /api/v1/rules¶
List all automation rules.
[
{
"id": "rule-001",
"name": "Urgent from Alice",
"enabled": true,
"trigger": "on_inbound",
"conditions": "from:Alice AND contains:urgent",
"action": {"type": "auto_reply", "body": "On my way!"}
}
]
POST /api/v1/rules¶
Create a new rule. Returns the created rule with its assigned id.
PUT /api/v1/rules/{id}¶
Update a rule.
DELETE /api/v1/rules/{id}¶
Delete a rule.
SSE stream¶
GET /api/logs¶
Server-Sent Events stream of structured log entries. Connect and keep the connection open to receive real-time events.
Each event is a JSON object:
data: {"ts": "2026-05-16T14:00:00Z", "source": "bridge", "level": "info", "msg": "new message from Alice"}
data: {"ts": "2026-05-16T14:00:01Z", "source": "ntfy", "level": "info", "msg": "notification sent"}
The React frontend subscribes to this stream to invalidate TanStack Query caches when new messages arrive.
GET /api/v1/events¶
Alternative SSE stream scoped to message events only (not log entries). Format:
data: {"type": "new_message", "chat_guid": "...", "rowid": 12345}
data: {"type": "message_sent", "handle": "+15551234567", "rowid": 12346}
UI-specific endpoints¶
GET /api/ui/stats¶
Message statistics (requires Stats plugin enabled).
{
"enabled": true,
"sent_total": 1234,
"received_total": 5678,
"top_contacts": [{"name": "Alice", "count": 200}],
"by_hour": [0, 5, 12, ...],
"by_day": [120, 180, ...]
}
GET /api/ui/themes¶
List available themes for the picker.
{
"built_in": ["dracula", "catppuccin-mocha", ...],
"packages": ["my-dark-theme"],
"active": "dracula"
}
MCP tools¶
The MCP stdio server exposes these tools to LLM clients. Start with chatwire mcp.
| Tool | Description | Scope |
|---|---|---|
list_conversations |
List recent conversations | read |
get_messages |
Get messages in a conversation | read |
send_message |
Send an iMessage or SMS | write |
search_messages |
Full-text search | read |
get_contact |
Look up a contact | read |
list_whitelist |
List whitelisted contacts | read |
See MCP integration guide for setup and authentication.