The Portal is the web UI and REST surface for the WPD Message Gateway when running in server mode.
Portal UI (http://localhost:10104)
| Feature | Description |
|---|---|
| Authentication | Register and sign in (email + password → JWT) |
| Workspaces | List, create, and join workspaces |
| Message logs | Outbound send request audit trail (Overview + per-channel tabs) |
| Email inbox | Browse captured outbound emails with live SSE updates |
| Email templates | Create and manage reusable email layouts |
| Integrations | Connect, activate, deactivate, or remove messaging providers |
| Settings | General workspace settings, API keys, team management, message dispatch mode |
| Get started | Curl examples for sending via the public gateway API |
Team management (members and role-based invitations) lives under Settings → Team Management. SMS, push, and chat show request logs in the UI; captured-message browsing for those channels is API-only.
| Feature | Portal UI | Description |
|---|---|---|
| Inbox capture | Email ✓ | Messages captured when message_dispatch_mode=memory or store_message_content=true — Inbox API |
| Internal ingest | Automation writes to inbox (requires Portal auth) | |
| Workspace API | Partial | Create/join workspaces and manage keys in the UI; additional endpoints in usage |
Provider credentials are stored in PostgreSQL (encrypted at rest) and managed on the Portal Integrations page. Dispatch settings are configured in Settings → Message Dispatch or via REST.
make start # Starts Go server + React Portal UIRegister (email + password), then sign in.
When a workspace uses message_dispatch_mode=memory, outbound messages are captured in-process RAM and displayed in the Portal inbox.
Your App
│
│ POST /v1/email (workspace: memory dispatch)
▼
Memory Provider
└──────────────────▶ Portal Inbox (REST + SSE)
└── "inbox" tab shows email
| Channel | List view | Detail view |
|---|---|---|
| Subject, recipient, preview | Full HTML render | |
| SMS | Full message inline | — |
| Push | Title, body, data | — |
| Chat | Message content | — |
Each workspace controls routing and inbox capture with two settings:
| Setting | Values | Role |
|---|---|---|
message_dispatch_mode |
memory (default) | provider |
Routing target |
store_message_content |
false (default) | true |
Capture message body in portal inbox when enabled |
Portal Settings → Message Dispatch: radio Memory / Provider, plus a checkbox for Store message content in inbox (see usage).
Set via Portal Settings → Message Dispatch or REST:
PATCH /api/v1/workspaces/:wid/settings
Content-Type: application/json
Authorization: Bearer <portal-jwt>
{ "message_dispatch_mode": "provider", "store_message_content": "false" }Request logs (message_request_logs) are always written for operational tracing.
The inbox and all Portal API endpoints are not public. Every request must be authenticated.
Authenticated via Portal JWT:
# 1. Register (first time) OR login
curl -X POST http://localhost:10101/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "your-password"}'
# → { "token": "<jwt>", "user": {...} }
# 2. Use JWT for all /api/v1/* requests
curl -H "Authorization: Bearer <jwt>" \
http://localhost:10101/api/v1/workspacesThree things are required:
- Portal JWT —
Authorization: Bearer <token>(identifies the user) - Workspace membership — user must be in
workspace_membersfor{wid} - Workspace API key —
X-Api-Client-Id+X-Api-Client-Secretfor that workspace
curl -H "Authorization: Bearer $PORTAL_JWT" \
-H "X-Api-Client-Id: $CLIENT_ID" \
-H "X-Api-Client-Secret: $SECRET" \
http://localhost:10101/api/v1/workspaces/$WORKSPACE_ID/inbox/emailsNote: For GET requests only, JWT may alternatively be passed as
?access_token=<jwt>(useful for SSE connections from browser EventSource). API key may also be passed as?client_id=&client_secret=— prefer headers over HTTPS.
Uses API key credentials + X-Workspace-Key header. See Usage Guide.
All paths are under /api/v1/workspaces/{workspaceId}/inbox/...
Required headers: Authorization: Bearer <portal-jwt>, X-Api-Client-Id, X-Api-Client-Secret
| Method | Endpoint | Description |
|---|---|---|
| GET | .../inbox/stats |
Message counts for this workspace |
| GET | .../inbox/emails |
List captured emails |
| GET | .../inbox/emails/{id} |
Get one email |
| DELETE | .../inbox/emails/{id} |
Delete one email |
| GET | .../inbox/sms |
List captured SMS |
| GET | .../inbox/sms/{id} |
Get one SMS |
| DELETE | .../inbox/sms/{id} |
Delete one SMS |
| GET | .../inbox/push |
List captured push notifications |
| GET | .../inbox/push/{id} |
Get one push notification |
| DELETE | .../inbox/push/{id} |
Delete one push notification |
| GET | .../inbox/chat |
List captured chat messages |
| GET | .../inbox/chat/{id} |
Get one chat message |
| DELETE | .../inbox/chat/{id} |
Delete one chat message |
| DELETE | .../inbox/messages |
Clear all captured messages for this workspace |
| GET | .../inbox/events |
SSE stream for real-time updates |
For writing directly into the inbox via external automation (e.g. from CI or background jobs):
POST /api/v1/workspaces/{workspaceId}/internal/email
POST /api/v1/workspaces/{workspaceId}/internal/sms
POST /api/v1/workspaces/{workspaceId}/internal/push
POST /api/v1/workspaces/{workspaceId}/internal/chat
These endpoints are protected by Portal JWT auth and workspace membership. They do not require the workspace API key headers used by inbox read routes.
# configs/local.yml
server:
port: 10101 # Send API + Portal REST API
portal:
jwt_secret: "your-secret-min-32-chars"
jwt_ttl_hours: 72
ui_port: 10104 # React dev server (Portal UI)Provider credentials (Mailgun API keys, etc.) are not in this file. Configure them on the Portal Integrations page or via the integrations REST API. Values are stored AES-encrypted in PostgreSQL.
- Backend: Go — Echo framework, memory provider, PostgreSQL repositories
- Frontend: React, TypeScript, Tailwind CSS, shadcn/ui, Vite
- Realtime: Server-Sent Events (SSE) for live inbox updates
- Usage Guide — Full API reference
- Architecture — System design
- E2E Testing — Using the inbox in CI/CD