HTTP endpoints
Besides gRPC and gRPC-Web, the backend answers a short, fixed list of plain HTTP routes on the same port. None of them is a data API. Campaigns, content, leads, and everything else are gRPC only.
| Route | Auth | Purpose |
|---|---|---|
GET /health | None | Always 200. Body: {"status":"ok","model_ready":<bool>,"port":<int>} |
GET /auth/config | None | {"google_client_id":"…","password_auth":<bool>} for the login screen |
POST /auth/google | None (it is the sign-in) | Exchanges a Google ID token for a PULSE session. Same JSON shape as Login. |
POST /auth/refresh | Refresh token | Exchanges a refresh token for a new pair in the same session. Refused once the session is signed out. |
/creatives/<name>, /media/<name> | The owner’s session, or a server-signed URL | Generated media, decrypted from the sealed store |
GET /podcast/feed.xml | None, on purpose | The podcast RSS feed that podcast platforms poll. Needs PULSE_PUBLIC_BASE_URL. |
POST /voice/webrtc/offer | Session | Forwards a WebRTC offer to the calling agent (CALLING_AGENT_URL). 503 when that is not set. |
POST /voice/transcribe | Session | Raw PCM in, {"text":"…"} out, via Voxtral |
GET /.well-known/agent-card.json | None | The A2A agent card |
/a2a | Session (bearer header only) | A2A JSON-RPC. See Calling the API. |
POST /twilio/message | Twilio signature | Inbound WhatsApp and SMS |
| anything else | None | The Flutter web app, when PULSE_WEB_DIR is set |
Requests with gRPC-Web content types go to the gRPC server before any of these routes are checked.