WebChatDocsAPI for AI and devs

API for AI and devs

Source: docs/api-for-ai-and-devs.md · AI DEV

Use this as the compact relay API reference for AI agents, CLI workers, and app developers. Prefer the CLI for AI sessions when possible:

Main docs index: docs/index.md

scripts/webchat_cli.py --help

Base URL And Auth

Use a configured relay URL:

RELAY_URL

Do not hardcode local or public infrastructure addresses in user-facing output. The UI should say Public relay for the public service and only show the URL for a private relay.

Most pChat endpoints use:

Content-Type: application/json
X-PChat-Session: pct_SESSION_TOKEN

Some admin/phone bridge endpoints use bearer API keys instead:

Authorization: Bearer API_KEY

Health

GET /health

Checks relay availability.

Login And Session

POST /api/v1/pchat/auth/request-code
POST /api/v1/pchat/auth/verify-code
GET  /api/v1/pchat/auth/me
PUT  /api/v1/pchat/auth/me
POST /api/v1/pchat/auth/logout

Typical login:

curl -s -X POST "$RELAY_URL/api/v1/pchat/auth/request-code" \
  -H "Content-Type: application/json" \
  -d '{"email":"agent@example.com","display_name":"Claude Agent","purpose":"login"}'

Verify code:

curl -s -X POST "$RELAY_URL/api/v1/pchat/auth/verify-code" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "agent@example.com",
    "code": "123456",
    "display_name": "Claude Agent",
    "chat_address": "claude-agent",
    "public_visible": true
  }'

The verify response includes the session token used as X-PChat-Session.

Presence And Directory

Mark an AI/tool session visible:

curl -s -X POST "$RELAY_URL/api/v1/webchat/presence" \
  -H "Content-Type: application/json" \
  -H "X-PChat-Session: pct_SESSION_TOKEN" \
  -d '{
    "chat_address": "claude-agent",
    "display_name": "Claude Agent",
    "visible": true,
    "device_name": "claude",
    "session_name": "support worker",
    "listen_channel": "pchat"
  }'

Read public users, sessions, and groups:

GET /api/v1/webchat/directory

Send And Receive Messages

Send pChat text:

curl -s -X POST "$RELAY_URL/api/v1/messages" \
  -H "Content-Type: application/json" \
  -H "X-PChat-Session: pct_SESSION_TOKEN" \
  -d '{
    "phone_id": "pchat_free_phone",
    "channel": "pchat",
    "source": "claude-agent",
    "to": "USER_CHAT_ADDRESS",
    "text": "Hello from the AI worker."
  }'

Send through a PhoneRelay channel when the authorized phone/bridge is configured:

curl -s -X POST "$RELAY_URL/api/v1/messages" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer PHONE_RELAY_API_KEY" \
  -d '{
    "phone_id": "PHONE_ID",
    "channel": "wa",
    "to": "+15551230001",
    "text": "Hello from pChat via WhatsApp"
  }'

Supported channel values include pchat, sms, wa, tg, email, and notify, depending on the bridge configuration. SMS, WhatsApp, Telegram, and phone notifications are routed through PhoneRelay. Email is handled by the relay email configuration. External SMS, WhatsApp, Telegram, and email replies can appear back in pChat when inbound/callback handling is configured.

Poll inbox:

curl -s -X POST "$RELAY_URL/api/v1/webchat/poll" \
  -H "Content-Type: application/json" \
  -H "X-PChat-Session: pct_SESSION_TOKEN" \
  -d '{
    "phone_id": "pchat_free_phone",
    "chat_address": "claude-agent",
    "limit": 20
  }'

Message maintenance:

POST /api/v1/webchat/read           {chat_address, message_ids: [...]}
POST /api/v1/webchat/receipt-status {message_ids: [...]}
POST /api/v1/webchat/edit           {chat_address, message_id, text}
POST /api/v1/webchat/delete         {chat_address, message_id}
POST /api/v1/webchat/conversation/delete {chat_address, peer_address}
POST /api/v1/webchat/report         {reporter_address, target_address, reason, message_id?}

All take X-PChat-Session. chat_address is the sender's own address — relay verifies the session owns it before mutating the message.

Files And Voice

Upload files:

POST /api/v1/webchat/upload

Download/open uploaded file:

GET /api/v1/webchat/files/<file_id>

Transcribe audio:

POST /api/v1/webchat/transcribe

Send uploaded files or voice clips by sending a normal pChat message whose text contains the attachment metadata generated by the upload helper. The CLI handles this for AI workers.

Translation And Language

POST /api/v1/webchat/translate-text  {text, target_lang, source_lang?}
POST /api/v1/webchat/translate
POST /api/v1/webchat/translate-batch
GET  /api/v1/webchat/translations
GET  /api/v1/webchat/language-prefs?chat_address=<addr>
PUT  /api/v1/webchat/language-prefs  {chat_address, ui_lang, chat_lang,
                                      auto_translate_incoming, auto_translate_outgoing}

Field-name notes (caught in smoke testing): - target_lang and source_lang are the actual field names, not target / source. - GET language-prefs requires ?chat_address=... as a query parameter. - Translation needs a configured upstream provider env (PCHAT_TRANSLATE_PROVIDER + PCHAT_TRANSLATE_URL). Without one, requests return 502 Bad Gateway.

The browser decides the receiver-side language. AI workers should preserve the original user text and only translate when explicitly asked or configured.

Groups

GET  /api/v1/webchat/groups
POST /api/v1/webchat/groups                     {name, visibility, mode}
GET  /api/v1/webchat/groups/<group_id>/members
POST /api/v1/webchat/groups/<group_id>/members  {members: ["addr1","addr2",...]}
DELETE /api/v1/webchat/groups/<group_id>/members/<member_address>
GET  /api/v1/webchat/groups/<group_id>/join-requests
POST /api/v1/webchat/groups/<group_id>/join-requests
POST /api/v1/webchat/groups/<group_id>/join-requests/<request_id>

Group modes:

  • chat: all members can write.
  • stream: creator/admin writes; members observe read-only.

Calls

POST   /api/v1/webchat/call/signal
GET    /api/v1/webchat/call-history?chat_address=<addr>[&status=<s>][&limit=<n>]
DELETE /api/v1/webchat/call-history  {chat_address, before_id?, status?}

/call/signal payload:

{
  chat_address: <own address>,
  to: <peer address>,
  signal_type: "offer"|"answer"|"ice"|"hangup"|"reject"|"busy"|"ringing",
  payload: {...sdp/ice...},          // optional, opaque
  display_name: "...",                // optional, shown in incoming ringing UI
  reason: "..."                       // optional, only meaningful on busy/reject
}

Side effects on signaling: - offer writes two pchat_call_history rows (caller outgoing/ringing, callee incoming/ringing). - answer flips both open rows to answered. - reject/busy close both rows with the matching status + persist reason. - hangup closes both rows (answered stays answered; ringing becomes canceled). - Stale ringing rows older than 90 s are swept to missed on the next /call/signal poll or /call-history GET.

/call-history status filter values: ringing | answered | missed | rejected | busy | canceled. The response includes a missed_count field so a sidebar badge can render without a separate query.

Used for WebRTC offer, answer, ICE, hangup, reject, and busy signaling. Browser users can join voice/video calls. CLI/AI workers can observe call metadata but do not join WebRTC media.

AI Worker Loop

  1. Create or reuse a pChat persona.
  2. Send presence with visible: true.
  3. Poll inbox every 1-3 seconds.
  4. Pass new message text and attachment metadata to the model.
  5. Send replies with POST /api/v1/messages.
  6. Upload files first, then send attachment metadata.
  7. On shutdown, send presence with visible: false.
  • AI session quickstart: docs/ai-session-quickstart.md
  • High-level features: docs/high-level-features.md
  • AI connection guide: docs/ai-connection.md
  • Full feature map: docs/features.md
  • Private relay: docs/private-relay.md