Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/chamilonster/Piumy/llms.txt

Use this file to discover all available pages before exploring further.

Complete reference for every REST endpoint exposed by the Piumy core. All /api/* paths require the X-API-Key header (or ?key= query param) when PIMYWA_API_KEY is set. The /healthz endpoint has no authentication requirement. All successful responses are application/json unless noted otherwise. Error responses follow the shape {"error": "<message>"}.

GET /healthz

Simple liveness check. Returns the plain text string ok with a 200 status. Use this for load balancer probes or systemd ExecStartPost checks. Auth: None
curl http://piumy.local:8080/healthz
ok

GET /api/status

Returns the current switchboard status snapshot — the same data written to status.json on disk. Includes WhatsApp connection state, queue depth, mood, battery, system metrics, and agent connectivity. See the status.json contract for the complete field reference. Auth: Required
curl http://piumy.local:8080/api/status \
  -H "X-API-Key: <key>"
{
  "mood": "few",
  "speech": "3 waiting",
  "queue": 3,
  "last_msg": "hey are you there?",
  "wa_connected": true,
  "show_qr": false,
  "qr_data": null,
  "battery": 82,
  "voltage": 3940,
  "charging": false,
  "time_remaining": 210,
  "cpu": 12,
  "ram": 44,
  "wifi": 3,
  "ip": "192.168.1.42",
  "hostname": "piumy.local",
  "ssid": "HomeWifi",
  "own_jid": "56999999999@s.whatsapp.net",
  "agent_connected": true,
  "agents": 1,
  "uptime": 3600,
  "reconnect_paused": false,
  "muted": false,
  "sent": 128,
  "face": "(◕‿◕)",
  "updated_at": "2026-03-15T14:22:05Z"
}

GET /api/chats

Lists recent chats, ordered by last activity. Returns metadata for each chat including JID, display name, current mode, and last message preview. Auth: Required
limit
integer
default:"20"
Maximum number of chats to return.
curl "http://piumy.local:8080/api/chats?limit=5" \
  -H "X-API-Key: <key>"
[
  {
    "jid": "56999999999@s.whatsapp.net",
    "name": "Alice",
    "mode": "advanced",
    "last_msg": "Can you check on my order?",
    "updated_at": "2026-03-15T14:20:00Z"
  },
  {
    "jid": "56988888888@s.whatsapp.net",
    "name": "Bob",
    "mode": "auto",
    "last_msg": "Thanks!",
    "updated_at": "2026-03-15T13:05:00Z"
  }
]

GET /api/messages

Returns messages from a specific chat, ordered newest-first. Auth: Required
chat
string
required
The chat’s JID (e.g. 56999999999@s.whatsapp.net). Required.
limit
integer
default:"20"
Maximum number of messages to return.
curl "http://piumy.local:8080/api/messages?chat=56999999999%40s.whatsapp.net&limit=10" \
  -H "X-API-Key: <key>"
[
  {
    "id": "3EB0A1B2C3D4E5F6",
    "chat": "56999999999@s.whatsapp.net",
    "from_me": false,
    "body": "Can you check on my order?",
    "timestamp": 1710509200,
    "handled": false
  },
  {
    "id": "3EB0A1B2C3D4E5F7",
    "chat": "56999999999@s.whatsapp.net",
    "from_me": true,
    "body": "Of course, let me look into that.",
    "timestamp": 1710508800,
    "handled": true
  }
]

GET /api/queue

Returns advanced-mode messages that are waiting for an agent to handle. This is the same set that drives the queue field in the status snapshot. Auth: Required
limit
integer
default:"20"
Maximum number of queued messages to return.
curl "http://piumy.local:8080/api/queue?limit=20" \
  -H "X-API-Key: <key>"
[
  {
    "id": "3EB0A1B2C3D4E5F6",
    "chat": "56999999999@s.whatsapp.net",
    "from_me": false,
    "body": "Can you check on my order?",
    "timestamp": 1710509200,
    "handled": false
  }
]

POST /api/send

Queues an outbound message. The gateway dispatches it with the configured anti-ban delay (PIMYWA_DELAY_DISPATCH_MIN / PIMYWA_DELAY_DISPATCH_MAX). The message is rate-limited by PIMYWA_RATE_LIMIT_PER_MIN and PIMYWA_RATE_LIMIT_PER_DAY. Rejected outright if the kill switch is on (muted: true). Auth: Required
To
string
required
Destination JID (e.g. 56999999999@s.whatsapp.net).
Message
string
required
Message text to send.
curl -X POST http://piumy.local:8080/api/send \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"To": "56999999999@s.whatsapp.net", "Message": "Hello!"}'
{"status": "queued"}

POST /api/mode

Sets a chat’s routing mode. auto enables the bridge for auto-replies; advanced puts the chat in the agent queue. Auth: Required
Chat
string
required
The chat’s JID.
Mode
string
required
auto or advanced.
curl -X POST http://piumy.local:8080/api/mode \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"Chat": "56999999999@s.whatsapp.net", "Mode": "advanced"}'
{"status": "ok", "mode": "advanced"}

POST /api/escalate

Convenience wrapper around POST /api/mode that always sets the chat to advanced. Equivalent to {"Chat": "...", "Mode": "advanced"} but more explicit about intent. Auth: Required
Chat
string
required
The chat’s JID.
curl -X POST http://piumy.local:8080/api/escalate \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"Chat": "56999999999@s.whatsapp.net"}'
{"status": "escalated"}

POST /api/handled

Marks a specific message as handled so it no longer appears in the advanced-mode queue. Auth: Required
Chat
string
required
The chat’s JID.
ID
string
required
The message ID to mark as handled.
curl -X POST http://piumy.local:8080/api/handled \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"Chat": "56999999999@s.whatsapp.net", "ID": "3EB0A1B2C3D4E5F6"}'
{"status": "ok"}

GET /api/battery/log

Returns a window of the discharge/charge trace CSV rows written by the power adapter (adapters/power/timeremain.py). Useful for rendering a time-series battery chart on the dashboard. Returns the most recent rows up to limit, oldest first. If PIMYWA_BATTERY_LOG_FILE is not configured or the file does not yet exist, this endpoint returns an empty array ([]) rather than an error — a missing trace is a normal degraded state, not a broken deployment. Auth: Required
limit
integer
default:"500"
Maximum number of trace rows to return (most recent window).
curl "http://piumy.local:8080/api/battery/log?limit=100" \
  -H "X-API-Key: <key>"
[
  {
    "ts": 1710508200,
    "voltage_mv": 3980,
    "raw_pct": 85,
    "linearized_pct": 82.4,
    "charging": false
  },
  {
    "ts": 1710508260,
    "voltage_mv": 3975,
    "raw_pct": 84,
    "linearized_pct": 81.9,
    "charging": false
  }
]
ts
integer
Unix timestamp of the sample.
voltage_mv
integer | null
Raw cell voltage in millivolts, or null if not available from the hardware.
raw_pct
integer
Raw voltage-curve battery percentage (can appear jumpy — prefer linearized_pct for display).
linearized_pct
number
Linearized battery percentage as estimated by the power adapter’s adaptive model.
charging
boolean
true if the power adapter inferred charging at this sample.

GET /api/events

Opens a Server-Sent Events (SSE) stream. The server emits a data: event on every inbound message nudge. A keep-alive comment (: keep-alive) is sent every 20 seconds so the connection stays alive through proxies and load balancers. Use this for real-time dashboard updates without polling GET /api/status. Auth: Required (key may be passed via ?key= for EventSource compatibility) Response content-type: text/event-stream
curl -N "http://piumy.local:8080/api/events?key=<key>"
: keep-alive

data: {"type":"message","chat":"56999999999@s.whatsapp.net"}

: keep-alive
When using the browser’s native EventSource API, the X-API-Key header cannot be set. Pass the key via ?key= instead. On a trusted LAN this is acceptable; be aware the key may appear in proxy access logs.

GET /api/qr.svg

Returns the current WhatsApp linking QR code as an SVG image. Returns 404 when no QR data is available (the gateway is not in QR/linking mode). Auth: Required Response content-type: image/svg+xml
curl http://piumy.local:8080/api/qr.svg \
  -H "X-API-Key: <key>" \
  -o qr.svg
HTTP/1.1 200 OK
Content-Type: image/svg+xml
Cache-Control: no-cache, no-store

<svg ...>...</svg>
Returns {"error": "no QR data"} with status 404 when the gateway is not in linking mode.

GET /api/settings

Returns the current runtime settings, including media download filters, anti-ban delay windows, and rate limits. Also includes the floors and ceilings enforced by the guardrails (delay floors can never be tightened below the shipped defaults; rate limit ceilings can never be loosened past the configured maximums). Auth: Required
curl http://piumy.local:8080/api/settings \
  -H "X-API-Key: <key>"
{
  "media_skip_video_group": false,
  "media_skip_video_chat": false,
  "media_skip_photo_group": false,
  "media_skip_photo_chat": false,
  "media_max_mb": 512,
  "dispatch_delay_min_sec": 1.0,
  "dispatch_delay_max_sec": 5.0,
  "read_delay_min_sec": 2.0,
  "read_delay_max_sec": 8.0,
  "action_delay_min_sec": 1.0,
  "action_delay_max_sec": 4.0,
  "rate_limit_per_min": 10,
  "rate_limit_per_day": 500,
  "floors": {
    "dispatch_delay_sec": 1.0,
    "read_delay_sec": 2.0,
    "action_delay_sec": 1.0,
    "media_max_mb": 3
  },
  "ceilings": {
    "rate_limit_per_min": 10,
    "rate_limit_per_day": 500
  }
}

POST /api/settings

Updates runtime settings. Changes take effect immediately without a restart. The response echoes the effective (post-guardrail-clamp) settings. Guardrails:
  • Delay windows can only be loosened (slower), never tightened below the shipped defaults.
  • Rate limits can only be tightened (lower), never raised past the configured ceilings.
  • media_max_mb is floored at a sane minimum so the media GC can never be effectively disabled.
Auth: Required
curl -X POST http://piumy.local:8080/api/settings \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{
    "media_skip_video_group": true,
    "media_max_mb": 256,
    "dispatch_delay_min_sec": 2.0,
    "dispatch_delay_max_sec": 8.0,
    "read_delay_min_sec": 3.0,
    "read_delay_max_sec": 10.0,
    "action_delay_min_sec": 1.0,
    "action_delay_max_sec": 4.0,
    "rate_limit_per_min": 5,
    "rate_limit_per_day": 200
  }'
Returns the same shape as GET /api/settings with the effective post-clamp values.

GET /api/killswitch

Returns the current state of the kill switch (mute). When the kill switch is on, send_message (via MCP) and POST /api/send are both rejected, and the status mood becomes muted. Auth: Required
curl http://piumy.local:8080/api/killswitch \
  -H "X-API-Key: <key>"
{"on": false}

POST /api/killswitch

Enables or disables the kill switch. The state is mirrored into status.json as muted so the display adapter can show the muted face. Auth: Required
On
boolean
required
true to mute all outbound sends; false to resume.
# Mute all sends
curl -X POST http://piumy.local:8080/api/killswitch \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"On": true}'
{"on": true}

GET /api/gateway

Returns the current WhatsApp gateway running state and connection information. Auth: Required
curl http://piumy.local:8080/api/gateway \
  -H "X-API-Key: <key>"
{
  "running": true,
  "wa_connected": true,
  "mood": "few",
  "own_jid": "56999999999@s.whatsapp.net"
}

POST /api/gateway

Starts or stops the WhatsApp gateway connection loop.
  • "link" → calls Start(): launches the connect/QR loop; arms the 3-minute QR exposure cap.
  • "disconnect" → calls Stop(): cancels the loop and resets state to idle.
Returns 503 if the gateway is not available. Auth: Required
Action
string
required
"link" to start the gateway or "disconnect" to stop it.
curl -X POST http://piumy.local:8080/api/gateway \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"Action": "link"}'
{
  "ok": true,
  "running": true,
  "wa_connected": false,
  "mood": "qr",
  "own_jid": ""
}

POST /api/reconnect

Clears the reconnect-paused state and restarts the gateway connection loop. Use this when reconnect_paused is true in the status snapshot (the anti-ban governor paused reconnection after too many consecutive failures). Returns 503 if the gateway is not available. Auth: Required Body: None
curl -X POST http://piumy.local:8080/api/reconnect \
  -H "X-API-Key: <key>"
{"status": "ok"}

GET /api/backup

Returns session backup status: whether backups are enabled, how many are retained, and the most recent backup’s metadata. Returns 503 if PIMYWA_BACKUP_KEY is not set (backups are disabled). Auth: Required
curl http://piumy.local:8080/api/backup \
  -H "X-API-Key: <key>"
{
  "enabled": true,
  "last_backup": "2026-03-15T00:00:00Z",
  "count": 5,
  "dir": "/opt/pimywa/data/backups"
}

POST /api/backup

Triggers a manual session backup immediately. Returns the resulting backup file metadata. Returns 503 if backups are disabled (no PIMYWA_BACKUP_KEY configured).
Session restore is deliberately not exposed over the REST API. It is a CLI-only operation (pimywa restore-session) — replacing the live session over the LAN is intentionally out of scope for this API.
Auth: Required Body: None
curl -X POST http://piumy.local:8080/api/backup \
  -H "X-API-Key: <key>"
{
  "path": "/opt/pimywa/data/backups/wa-session-2026-03-15T14-22-05Z.enc",
  "size": 32768,
  "created_at": "2026-03-15T14:22:05Z"
}

GET /api/mcp-guard

Returns the MCP anti-flood limiter status: the effective configuration plus every currently-tracked MCP client (session ID, throttle/block state). Session IDs are opaque per-connection identifiers — never the auth token. Returns 503 if the guard is not available. Auth: Required
curl http://piumy.local:8080/api/mcp-guard \
  -H "X-API-Key: <key>"
{
  "rate_per_min": 120,
  "emit_rate_per_min": 20,
  "block_threshold": 5,
  "block_cooldown_sec": 300,
  "clients": []
}

POST /api/mcp-guard

Updates the MCP anti-flood thresholds at runtime. Changes take effect immediately. The response echoes the effective post-clamp values. Guardrails:
  • rate_per_min: floored at 10, ceilinged at 600.
  • emit_rate_per_min: floored at 2, ceilinged at 100.
  • block_threshold: floored at 1, ceilinged at 20.
  • block_cooldown_sec: floored at 30 seconds, ceilinged at 3600 seconds (1 hour).
Returns 503 if the guard is not available. Auth: Required
rate_per_min
integer
Maximum general tool calls per minute per MCP session.
emit_rate_per_min
integer
Maximum emit tool calls (send_message, escalate) per minute per session.
block_threshold
integer
Consecutive throttled calls that trip the circuit breaker.
block_cooldown_sec
number
How long (in seconds) a tripped session remains blocked.
curl -X POST http://piumy.local:8080/api/mcp-guard \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{
    "rate_per_min": 60,
    "emit_rate_per_min": 10,
    "block_threshold": 3,
    "block_cooldown_sec": 120
  }'
Returns the same shape as GET /api/mcp-guard with the effective post-clamp values.

GET /api/router

Returns the current in-memory router configuration: the whitelist, allow_all flag, default_mode, and routes. See router.json reference for the full field documentation. Auth: Required
curl http://piumy.local:8080/api/router \
  -H "X-API-Key: <key>"
{
  "allow_all": false,
  "default_mode": "advanced",
  "whitelist": [
    "56999999999@s.whatsapp.net"
  ],
  "routes": [
    {
      "match": "56999999999@s.whatsapp.net",
      "mode": "advanced",
      "plugin": "mcp-agent",
      "model": "claude-opus-4-8"
    },
    {
      "match": "*",
      "mode": "advanced",
      "plugin": "none",
      "model": ""
    }
  ]
}

POST /api/router/whitelist

Adds or removes a JID from the in-memory whitelist and persists the change to router.json immediately. The gateway sees the change at once — no restart required. Returns 503 if the router manager is not available. Auth: Required
jid
string
required
The JID to add or remove (e.g. 56999999999@s.whatsapp.net).
action
string
required
"add" or "remove". The add action is idempotent — adding an already-present JID is a no-op.
curl -X POST http://piumy.local:8080/api/router/whitelist \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"jid": "56999999999@s.whatsapp.net", "action": "add"}'
Returns the updated router config snapshot (same shape as GET /api/router).

POST /api/router/allowall

Enables or disables the allow_all flag. When true, the whitelist check is skipped entirely — any number can send messages through the gateway. The change is persisted to router.json immediately.
Setting allow_all to true is risky — any phone number can reach your switchboard. Use only for development and testing.
Returns 503 if the router manager is not available. Auth: Required
On
boolean
required
true to allow all inbound senders; false to re-enable the whitelist.
curl -X POST http://piumy.local:8080/api/router/allowall \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"On": false}'
Returns the updated router config snapshot (same shape as GET /api/router).

GET /api/rules

Returns the current global and by-type rules tiers. A chat with no per-chat rules inherits from the by-type tier (based on whether it is a group or individual chat); if that is also empty, the global default applies. Auth: Required
curl http://piumy.local:8080/api/rules \
  -H "X-API-Key: <key>"
{
  "individual": "Reply concisely and helpfully.",
  "group": "Only respond when directly addressed.",
  "default": ""
}

POST /api/rules/type

Sets the by-type rules tier for individual or group chats. Applies to all chats of that type that have no per-chat rules of their own. Auth: Required
type
string
required
"individual" or "group".
rules
string
required
The rules text to set. Pass an empty string to clear.
curl -X POST http://piumy.local:8080/api/rules/type \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"type": "individual", "rules": "Reply concisely and helpfully."}'
{"status": "ok"}

POST /api/rules/default

Sets the global default rules — the catch-all for chats that have neither per-chat rules nor matching by-type rules. Auth: Required
rules
string
required
The default rules text. Pass an empty string to clear.
curl -X POST http://piumy.local:8080/api/rules/default \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"rules": "Be polite and brief."}'
{"status": "ok"}

POST /api/chats/boss

Marks or unmarks a chat as the trusted owner (is_boss). The owner chat receives privileged commands. This endpoint is deliberately not reachable from MCP — only the REST API (dashboard/owner) may set the boss flag. Auth: Required
jid
string
required
The chat’s JID.
boss
boolean
required
true to grant owner trust; false to revoke it.
curl -X POST http://piumy.local:8080/api/chats/boss \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"jid": "56999999999@s.whatsapp.net", "boss": true}'
{"status": "ok"}

POST /api/chats/rules

Sets per-chat behavior rules — instructions for how the AI should respond to this specific chat. Overrides the by-type and default rules tiers for this chat. This endpoint is deliberately not reachable from MCP — an agent must never rewrite the rules it is judged against. Auth: Required
jid
string
required
The chat’s JID.
rules
string
required
The rules text. Pass an empty string to clear per-chat rules (the chat then inherits from the by-type or default tier).
curl -X POST http://piumy.local:8080/api/chats/rules \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"jid": "56999999999@s.whatsapp.net", "rules": "This is a VIP customer. Always be warm and proactive."}'
{"status": "ok"}

POST /api/chats/description

Sets a chat’s description — a note about the chat (for a 1-1 contact) or the WhatsApp group topic (for a group). The description is visible to the AI via MCP context. Auth: Required
jid
string
required
The chat’s JID.
description
string
required
The description text. Pass an empty string to clear.
curl -X POST http://piumy.local:8080/api/chats/description \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"jid": "56999999999@s.whatsapp.net", "description": "VIP customer, orders frequently."}'
{"status": "ok"}

POST /api/chats/confirmation

Sets a chat’s confirmation baseline and default confirmer. Determines whether auto-reply drafts for this chat require owner approval before sending. This endpoint is deliberately not reachable from MCP — an agent must not be able to change its own confirmation baseline. Auth: Required
jid
string
required
The chat’s JID.
mode
string
required
"none" (send drafts automatically) or "required" (hold drafts for owner approval).
confirmer
string
Optional JID of the default confirmer (the contact who approves drafts). Leave empty to use the system default.
curl -X POST http://piumy.local:8080/api/chats/confirmation \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"jid": "56999999999@s.whatsapp.net", "mode": "required"}'
{"status": "ok"}

POST /api/drafts/approve

Approves a pending auto-reply draft and moves it into the outbox for sending (with the normal anti-ban pacing). Optionally edits the draft text before approval. This endpoint is deliberately not reachable from MCP — only the owner approves drafts. Auth: Required
id
integer
required
The draft ID (integer).
text
string
Optional edited text. When provided, replaces the original draft text before sending.
curl -X POST http://piumy.local:8080/api/drafts/approve \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"id": 42, "text": "Sure, I will check on that right away!"}'
{"status": "ok"}
Returns 404 if the draft is not found or is no longer pending.

POST /api/drafts/discard

Marks a pending auto-reply draft as discarded — it will never be sent. The action is permanent. This endpoint is deliberately not reachable from MCP. Auth: Required
id
integer
required
The draft ID (integer).
curl -X POST http://piumy.local:8080/api/drafts/discard \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"id": 42}'
{"status": "ok"}
Returns 404 if the draft is not found or is no longer pending.

Build docs developers (and LLMs) love