Webmail
IMAP/SMTP mailboxes, OAuth mailbox connect, message read and send, and AI assists.
25 endpoints· 2 called live· 21 not exercised here· verified against http://127.0.0.1:8741
25 endpoints
GET/api/v1/salesshift/webmail/accountsCalled liveMailboxes visible to the caller (shared, unowned, or their own).
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"success": "bool",
"data": [
{
"id": "str",
"email": "str",
"display_name": "str",
"provider": "str",
"is_shared": "bool",
"is_active": "bool",
"signature": "str",
"imap_host": "str",
"imap_port": "int",
"smtp_host": "str",
"smtp_port": "int",
"last_sync_at": "str",
"sync_status": "str",
"sync_error": "null",
"auth_method": "str",
"oauth_provider": "null",
"oauth_scopes": "null",
"oauth_connected_at": "null",
"oauth_token_expires_at": "null",
"oauth_error": "null",
"token_health": {
"state": "...",
"message": "...",
"expires_at": "...",
"expires_in_seconds": "..."
},
"needs_reconnect": "bool"
},
"...x6"
],
"internal_domains": [
"str",
"...x3"
]
}POST/api/v1/salesshift/webmail/accountsNot exercised hereAdd a custom IMAP/SMTP mailbox; the credentials are proven before the row is saved.
Request body (required) — AccountIn
| Field | Type | Required | Notes |
|---|---|---|---|
| string | required | ||
| display_name | string? | optional | |
| password | string | required | |
| imap_host | string | required | |
| imap_port | integer | optional | Default 993. |
| smtp_host | string | required | |
| smtp_port | integer | optional | Default 587. |
| smtp_security | string | optional | Default "starttls". |
| is_shared | boolean | optional | Default true. |
| signature | string? | optional |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "email": "<string>", "display_name": "<string>", "password": "<string>", "imap_host": "<string>", "imap_port": 993, "smtp_host": "<string>", "smtp_port": 587, "smtp_security": "starttls", "is_shared": true, "signature": "<string>" }'Response · 201
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 400 | IMAP login failed: {...} |
PATCH/api/v1/salesshift/webmail/accounts/{account_id}Not exercised hereUpdate a mailbox's settings.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Request body (required) — AccountPatch
| Field | Type | Required | Notes |
|---|---|---|---|
| display_name | string? | optional | |
| is_shared | boolean? | optional | |
| is_active | boolean? | optional | |
| signature | string? | optional |
Values in the request below are typed placeholders, not sample data.
Request
curl -X PATCH 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "display_name": "<string>", "is_shared": false, "is_active": false, "signature": "<string>" }'Response · 200
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 404 | Mailbox account not found |
DELETE/api/v1/salesshift/webmail/accounts/{account_id}Not exercised hereRemove a mailbox, optionally deleting the underlying provisioned mailbox.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| delete_mailbox | boolean | optional | Default false. |
Request
curl -X DELETE 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"deleted_mailbox": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 404 | Mailbox account not found |
POST/api/v1/salesshift/webmail/accounts/{account_id}/actionNot exercised hereBulk message action (read, flag, move, delete, folder operations).
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Request body (required) — ActionIn
| Field | Type | Required | Notes |
|---|---|---|---|
| folder | string | required | |
| uids | integer[] | optional | Default []. |
| action | string | required | |
| category | string? | optional | |
| target_folder | string? | optional | |
| snooze_until | string(date-time)? | optional |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/action' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "folder": "<string>", "uids": [], "action": "<string>", "category": "<string>", "target_folder": "<string>", "snooze_until": "<string(date-time)>" }'Response · 200
{
"applied": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 400 | No messages selected |
| 400 | category required |
| 400 | target_folder required for move |
| 400 | Message is already in that folder |
| 400 | snooze_until required for snooze |
| 400 | snooze_until must be in the future |
| 400 | Unknown action: {...} |
GET/api/v1/salesshift/webmail/accounts/{account_id}/attachmentNot exercised hereDownload one attachment from a message by index.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| folder | string | required | |
| uid | integer | required | |
| index | integer | optional | Default 0. |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/attachment?folder=<string>&uid=<integer>' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 404 | Attachment not found |
GET/api/v1/salesshift/webmail/accounts/{account_id}/foldersNot exercised hereList the IMAP folders on a mailbox.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/folders' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"data": "…",
"success": "…"
}GET/api/v1/salesshift/webmail/accounts/{account_id}/messageNot exercised hereOne full message by UID, optionally marking it seen.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| folder | string | required | |
| uid | integer | required | |
| mark_seen | boolean | optional | Default true. |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/message?folder=<string>&uid=<integer>' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 404 | Message not found |
GET/api/v1/salesshift/webmail/accounts/{account_id}/messagesNot exercised herePaged message overviews from one IMAP folder, with search and filter.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| folder | string | optional | Default "INBOX". |
| page | integer | optional | Default 1. |
| per_page | integer | optional | Default 10. |
| q | string | optional | Default "". |
| filter | string | optional | Default "all". |
| category | string | optional | Default "". |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/messages' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"data": "…",
"page": "…",
"per_page": "…",
"success": "…",
"total": "…"
}POST/api/v1/salesshift/webmail/accounts/{account_id}/passwordNot exercised hereSelf-service mailbox password change.
Self-service mailbox password change. internal → changes the REAL mailbox login via the mailadmin script (mailcow), then updates Vault. Dovecot may honor the old password for a few minutes (auth cache). custom → the password must already be changed at the provider; we verify it over IMAP, then update Vault.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Request body (required) — PasswordIn
| Field | Type | Required | Notes |
|---|---|---|---|
| new_password | string | required |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/password' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "new_password": "<string>" }'Response · 200
{
"provider": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 404 | Mailbox account not found |
| 403 | This mailbox is private to another user |
| 400 | {...} is connected with {...} — it has no password here. Use Reconnect to refresh the connection. |
| 502 | Mailbox password change failed: {...} |
| 400 | IMAP login with the new password failed — change it at your provider first, then update it here. ({...}) |
GET/api/v1/salesshift/webmail/accounts/{account_id}/rawNot exercised hereThe original RFC 822 source — "show original" / view-source.
The original RFC 822 source — "show original" / view-source. Returns text/plain rather than the usual JSON envelope: this is the raw message, headers and all, exactly as it arrived.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| folder | string | required | |
| uid | integer | required |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/raw?folder=<string>&uid=<integer>' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
Response carrying the raw RFC-822 message source.
Errors
| Status | detail |
|---|---|
| 404 | Message not found |
POST/api/v1/salesshift/webmail/accounts/{account_id}/reconnectKnown unprovenRe-consent an OAuth mailbox whose refresh token was revoked or expired.
Re-consent an OAuth mailbox whose refresh token was revoked or expired. Bound to this account id, so the callback refuses a grant for a different address instead of silently repointing a shared mailbox at whoever happened to be signed in to the browser.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Request body (optional) — OAuthConnectIn
| Field | Type | Required | Notes |
|---|---|---|---|
| return_to | string | optional | Default "/mail". |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/reconnect' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "return_to": "/mail" }'Response · 200
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 404 | Mailbox account not found |
| 403 | This mailbox is private to another user |
| 400 | {...} is a password mailbox. Update its password instead, or connect it again through Microsoft or Google. |
| 400 | computed at runtime: str(exc) |
GET/api/v1/salesshift/webmail/accounts/{account_id}/reply-contextNot exercised hereEverything a composer needs to answer a message: recipients worked out the way Gmail works them out, the ``Re:``/``Fwd:`` subject, the quoted original, and the threading headers.
Everything a composer needs to answer a message: recipients worked out the way Gmail works them out, the ``Re:``/``Fwd:`` subject, the quoted original, and the threading headers. ``attachments`` carry a ``folder``/``uid``/``index`` reference — POST them back as ``forward_attachments`` and the server re-reads them from IMAP, so the browser never has to download and re-upload the payload.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| folder | string | required | |
| uid | integer | required | |
| mode | string | optional | Default "reply". |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/reply-context?folder=<string>&uid=<integer>' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 400 | mode must be reply, reply_all or forward |
| 404 | Message not found |
GET/api/v1/salesshift/webmail/accounts/{account_id}/searchNot exercised hereIMAP SEARCH across several folders at once, merged newest-first.
IMAP SEARCH across several folders at once, merged newest-first. ``folders`` is a comma-separated list (default INBOX,Sent,Archive). A folder the mailbox doesn't have is skipped rather than failing the search.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| q | string | optional | Default "". |
| folders | string | optional | Default "". |
| limit | integer | optional | Default 50. |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/search' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"data": "…",
"folders": "…",
"folders_matched": "…",
"query": "…",
"success": "…",
"total": "…"
}Errors
| Status | detail |
|---|---|
| 400 | A search term is required |
POST/api/v1/salesshift/webmail/accounts/{account_id}/sendNot exercised hereSend or save-as-draft a message from a mailbox.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Request body (required) — app__services__salesshift__webmail_router__SendIn
| Field | Type | Required | Notes |
|---|---|---|---|
| to | string[] | required | |
| cc | string[] | optional | Default []. |
| bcc | string[] | optional | Default []. |
| subject | string | optional | Default "". |
| body_html | string | optional | Default "". |
| attachments | object[] | optional | Default []. |
| mode | string | optional | Default "send". |
| send_at | string(date-time)? | optional | |
| in_reply_to | string? | optional | |
| references | string? | optional | |
| forward_attachments | object[] | optional | Default []. |
| draft_uid | integer? | optional | |
| append_signature | boolean | optional | Default true. |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/send' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "to": [ "<string>" ], "cc": [], "bcc": [], "subject": "", "body_html": "", "attachments": [], "mode": "send", "send_at": "<string(date-time)>", "in_reply_to": "<string>", "references": "<string>", "forward_attachments": [], "draft_uid": 0, "append_signature": true }'Response · 200
{
"draft_uid": "…",
"mode": "…",
"replaced_uid": "…",
"send_at": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 400 | At least one recipient required |
| 400 | send_at required for scheduled send |
GET/api/v1/salesshift/webmail/accounts/{account_id}/threadNot exercised hereFull conversation for a message — the email/reply/email trail, Gmail-style.
Full conversation for a message — the email/reply/email trail, Gmail-style. Threading: Message-ID / In-Reply-To / References graph expanded to a fixed point across INBOX and Sent, plus a normalized-subject fallback restricted to the thread's correspondents. The requested message is marked seen.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| folder | string | required | |
| uid | integer | required |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/thread?folder=<string>&uid=<integer>' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"count": "…",
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 404 | Message not found |
POST/api/v1/salesshift/webmail/accounts/provisionNot exercised hereCreate a REAL mailbox on the platform mail stack (mailcow + relay spool + inbound sync job) and connect it — the user never touches SOGo.
Create a REAL mailbox on the platform mail stack (mailcow + relay spool + inbound sync job) and connect it — the user never touches SOGo.
Request body (required) — ProvisionIn
| Field | Type | Required | Notes |
|---|---|---|---|
| local_part | string | required | ≤ 64 chars. |
| domain | string | required | |
| display_name | string | optional | Default "". |
| password | string | required | |
| is_shared | boolean | optional | Default true. |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/provision' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "local_part": "<string>", "domain": "<string>", "display_name": "", "password": "<string>", "is_shared": true }'Response · 201
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 400 | Domain must be one of: {...} |
| 400 | Invalid mailbox name |
| 409 | Mailbox already connected |
| 502 | Mailbox provisioning failed: {...} |
POST/api/v1/salesshift/webmail/ai/improvePartialRewrite a draft body with the org's AI provider at a chosen tone, language and length.
Request body (required) — ImproveIn
| Field | Type | Required | Notes |
|---|---|---|---|
| text | string | required | |
| tone | string | optional | Default "friendly". |
| language | string | optional | Default "English". |
| length | string | optional | Default "medium". |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/ai/improve' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "text": "<string>", "tone": "friendly", "language": "English", "length": "medium" }'Response · 200
{
"data": "…",
"success": "…"
}POST/api/v1/salesshift/webmail/ai/summarize-unreadPartialSummarise up to 15 unread messages with the org's AI provider.
Request body (required) — SummarizeIn
| Field | Type | Required | Notes |
|---|---|---|---|
| account_id | string(uuid) | required | |
| folder | string | optional | Default "INBOX". |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/ai/summarize-unread' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "account_id": "<string(uuid)>", "folder": "INBOX" }'Response · 200
{
"data": "…",
"success": "…"
}GET/api/v1/salesshift/webmail/categoriesCalled liveThe organization's webmail categories, seeding the defaults on first use.
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/categories' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"success": "bool",
"data": [
{
"id": "str",
"name": "str",
"color": "str"
},
"...x5"
]
}POST/api/v1/salesshift/webmail/categoriesNot exercised hereCreate a webmail category.
Request body (required) — CategoryIn
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | required | |
| color | string | optional | Default "#6b7280". |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/categories' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "name": "<string>", "color": "#6b7280" }'Response · 201
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 409 | Category already exists |
DELETE/api/v1/salesshift/webmail/categories/{category_id}Not exercised hereDelete a webmail category.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| category_id | string(uuid) | required |
Request
curl -X DELETE 'https://api.vxcloud.io/api/v1/salesshift/webmail/categories/{category_id}' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"success": "…"
}POST/api/v1/salesshift/webmail/oauth/{provider}/connectKnown unprovenStart connecting a mailbox — returns the URL to send the user to.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| provider | string | required |
Request body (optional) — OAuthConnectIn
| Field | Type | Required | Notes |
|---|---|---|---|
| return_to | string | optional | Default "/mail". |
Values in the request below are typed placeholders, not sample data.
Request
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webmail/oauth/{provider}/connect' \
-H "Authorization: Bearer $SS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "return_to": "/mail" }'Response · 200
{
"data": "…",
"success": "…"
}Errors
| Status | detail |
|---|---|
| 400 | computed at runtime: str(exc) |
GET/api/v1/salesshift/webmail/oauth/callbackurl-secret-onlyKnown unprovenWhere the provider sends the browser back.
Where the provider sends the browser back. Unauthenticated by necessity — this is a top-level redirect from Microsoft or Google, carrying no Authorization header. The tenant comes from the HMAC-signed state plus a one-shot server-side record, which is why neither can be forged and neither can be replayed.
The provider redirects here with code + state; the state value is what ties the callback to the request that started it.
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
| code | string? | optional | |
| state | string? | optional | |
| error | string? | optional | |
| error_description | string? | optional |
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/oauth/callback'Response · 200
RedirectResponse back to the frontend — to the success target, or through _oauth_error_redirect on failure.
GET/api/v1/salesshift/webmail/oauth/providersKnown unprovenWhich mailbox providers this deployment can actually connect.
Which mailbox providers this deployment can actually connect. Reports ``configured: false`` with the missing pieces rather than hiding an unregistered provider — a Connect button that dies after the user has typed their password into Microsoft is worse than no button.
Request
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/oauth/providers' \
-H "Authorization: Bearer $SS_TOKEN"Response · 200
{
"success": "bool",
"data": [
{
"provider": "str",
"label": "str",
"configured": "bool",
"missing": [
"<empty list>"
],
"scopes": [
"...",
"...x6"
],
"redirect_uri": "str",
"imap_host": "str",
"smtp_host": "str"
},
"...x2"
]
}