API
Clients talk only to the api at https://mail.phonemail.net/api. The full reference, generated from docs-site/static/api/openapi.yaml (OpenAPI 3.1) and validated with Redocly, is in Swagger UI.
Conventions
| Topic | Rule |
|---|---|
| Auth | Authorization: Bearer <token> from any /auth/* sign-in call; tokens last 30 days |
| Client header | Send X-Client: web, portal or mobile; it is recorded as created_via when an account is created |
| People | Anywhere a person is named you may send 9884690127, 09884690127, +919884690127, a PhoneMail address or alias, or any outside email address (e.g. friend@gmail.com) |
| Errors | JSON {"error": "human-readable message"} with 400, 401, 403, 404, 409, 422 or 501 |
| Paging | Lists return newest first; pass the sentAt/lastMessageAt of the last item as before for the next page |
| Live updates | Socket.IO at path /api/socket.io, auth: {token}; event mail = something new for you |
Endpoint groups
| Group | Endpoints |
|---|---|
| Sign-up and sign-in | GET /auth/mode, POST /auth/password/register, POST /auth/password/login, POST /auth/otp/start, POST /auth/otp/verify, POST /auth/join/start, GET /auth/join/{code} |
| Twilio | POST /twilio/sms (webhook, signed) |
| Profile | GET /me, PATCH /me, PUT /me/password, POST /me/aliases, DELETE /me/aliases/{alias} |
| Conversations | GET /conversations, GET /conversations/{id}/messages, GET /threads/{rootId}, GET /lookup |
| Messages | POST /messages, POST /messages/{id}/reply, GET /messages/{id}, PATCH /mailbox/{messageId}, GET /folders/{folder}, GET /search |
| Drafts | GET /drafts, POST /drafts, PUT /drafts/{id}, DELETE /drafts/{id} |
| Groups | POST /groups, GET /groups/{id}, POST /groups/{id}/members, DELETE /groups/{id}/members/{userId}, POST /groups/{id}/leave, PATCH /groups/{id}/members/{userId} |
The internal mail-service contract
The api forwards mail calls to the Go service with X-Internal-Token and X-User-Id. These are the spec's §7 endpoints; they are not reachable from outside.
| Method and path | Purpose |
|---|---|
POST /messages | Deliver a new message (spec §6.1) |
POST /messages/{id}/reply | Reply inside a chat (§6.2) |
GET /conversations?filter=&before=&limit= | Home list |
GET /conversations/{id}/messages?before=&limit= | Open a chat (marks read) |
GET /threads/{rootId} | Whole thread in path order |
GET /messages/{id} | Traditional view with recipients (Bcc filtered) |
PATCH /mailbox/{messageId} | Read, favourite, folder |
GET /folders/{spam or trash}, GET /search?q=, GET /lookup?address= | Folders, full-text search, find a direct chat |
GET, POST, PUT, DELETE /drafts | Drafts |
POST /groups, members, leave, roles | Group management (§6.7) |
GET /health | Liveness (also checks the database) |
The mail service calls back POST {api}/internal/events after each delivery commits.
Mail protocols (for Postfix and Roundcube)
| Port | Protocol | Used by |
|---|---|---|
127.0.0.1:2525 | SMTP (no auth) | Postfix hands over mail for @phonemail.net; unknown recipients get 550 |
:1143 | IMAP4rev1 + MOVE | Roundcube; folders INBOX, Sent, Drafts, Junk, Trash; \Seen, \Flagged, \Answered map to read, starred, replied |
:1587 | SMTP submission, AUTH PLAIN/LOGIN | Roundcube sending; MAIL FROM must be the signed-in address |
:1180 | HTTP POST /password (form: user, curpass, newpass) | Roundcube's password plugin (httpapi driver) |
IMAP, submission and the password endpoint listen only on the Docker bridge address (WEBMAIL_BIND).