Skip to content
SSalesShiftDocs
API reference

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).
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Observed shape
{
  "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"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-webmail-accountswebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1178Called against http://127.0.0.1:8741 on 2026-08-06 with a real JWT; the response matched the shape recorded below.
POST/api/v1/salesshift/webmail/accountsNot exercised hereAdd a custom IMAP/SMTP mailbox; the credentials are proven before the row is saved.
Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Request body (required) — AccountIn

FieldTypeRequiredNotes
emailstringrequired
display_namestring?optional
passwordstringrequired
imap_hoststringrequired
imap_portintegeroptionalDefault 993.
smtp_hoststringrequired
smtp_portintegeroptionalDefault 587.
smtp_securitystringoptionalDefault "starttls".
is_sharedbooleanoptionalDefault true.
signaturestring?optional

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400IMAP login failed: {...}
#post-api-v1-salesshift-webmail-accountswebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1193Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
PATCH/api/v1/salesshift/webmail/accounts/{account_id}Not exercised hereUpdate a mailbox's settings.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Request body (required) — AccountPatch

FieldTypeRequiredNotes
display_namestring?optional
is_sharedboolean?optional
is_activeboolean?optional
signaturestring?optional

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Mailbox account not found
#patch-api-v1-salesshift-webmail-accounts-account-idwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1297Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
DELETE/api/v1/salesshift/webmail/accounts/{account_id}Not exercised hereRemove a mailbox, optionally deleting the underlying provisioned mailbox.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Query parameters

NameTypeRequiredNotes
delete_mailboxbooleanoptionalDefault false.

Request

curl
curl -X DELETE 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "deleted_mailbox": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Mailbox account not found
#delete-api-v1-salesshift-webmail-accounts-account-idwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1396Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
POST/api/v1/salesshift/webmail/accounts/{account_id}/actionNot exercised hereBulk message action (read, flag, move, delete, folder operations).
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Request body (required) — ActionIn

FieldTypeRequiredNotes
folderstringrequired
uidsinteger[]optionalDefault [].
actionstringrequired
categorystring?optional
target_folderstring?optional
snooze_untilstring(date-time)?optional

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "applied": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400No messages selected
400category required
400target_folder required for move
400Message is already in that folder
400snooze_until required for snooze
400snooze_until must be in the future
400Unknown action: {...}
#post-api-v1-salesshift-webmail-accounts-account-id-actionwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:2108Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
GET/api/v1/salesshift/webmail/accounts/{account_id}/attachmentNot exercised hereDownload one attachment from a message by index.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Query parameters

NameTypeRequiredNotes
folderstringrequired
uidintegerrequired
indexintegeroptionalDefault 0.

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/attachment?folder=<string>&uid=<integer>' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Attachment not found
#get-api-v1-salesshift-webmail-accounts-account-id-attachmentwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1866Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
GET/api/v1/salesshift/webmail/accounts/{account_id}/foldersNot exercised hereList the IMAP folders on a mailbox.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/folders' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#get-api-v1-salesshift-webmail-accounts-account-id-folderswebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1611Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
GET/api/v1/salesshift/webmail/accounts/{account_id}/messageNot exercised hereOne full message by UID, optionally marking it seen.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Query parameters

NameTypeRequiredNotes
folderstringrequired
uidintegerrequired
mark_seenbooleanoptionalDefault true.

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/message?folder=<string>&uid=<integer>' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Message not found
#get-api-v1-salesshift-webmail-accounts-account-id-messagewebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1684Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
GET/api/v1/salesshift/webmail/accounts/{account_id}/messagesNot exercised herePaged message overviews from one IMAP folder, with search and filter.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Query parameters

NameTypeRequiredNotes
folderstringoptionalDefault "INBOX".
pageintegeroptionalDefault 1.
per_pageintegeroptionalDefault 10.
qstringoptionalDefault "".
filterstringoptionalDefault "all".
categorystringoptionalDefault "".

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/messages' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "data": "…",
  "page": "…",
  "per_page": "…",
  "success": "…",
  "total": "…"
}
Top-level keys read from the handler's return statements.
#get-api-v1-salesshift-webmail-accounts-account-id-messageswebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1630Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
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.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Request body (required) — PasswordIn

FieldTypeRequiredNotes
new_passwordstringrequired

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "provider": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Mailbox account not found
403This mailbox is private to another user
400{...} is connected with {...} — it has no password here. Use Reconnect to refresh the connection.
502Mailbox password change failed: {...}
400IMAP login with the new password failed — change it at your provider first, then update it here. ({...})
#post-api-v1-salesshift-webmail-accounts-account-id-passwordwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1324Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
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.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
binary

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Query parameters

NameTypeRequiredNotes
folderstringrequired
uidintegerrequired

Request

curl
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

Statusdetail
404Message not found
#get-api-v1-salesshift-webmail-accounts-account-id-rawwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1893Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
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.

Known unproven. OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Request body (optional) — OAuthConnectIn

FieldTypeRequiredNotes
return_tostringoptionalDefault "/mail".

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Mailbox account not found
403This mailbox is private to another user
400{...} is a password mailbox. Update its password instead, or connect it again through Microsoft or Google.
400computed at runtime: str(exc)
#post-api-v1-salesshift-webmail-accounts-account-id-reconnectwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1505OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.
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.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Query parameters

NameTypeRequiredNotes
folderstringrequired
uidintegerrequired
modestringoptionalDefault "reply".

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400mode must be reply, reply_all or forward
404Message not found
#get-api-v1-salesshift-webmail-accounts-account-id-reply-contextwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1809Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
POST/api/v1/salesshift/webmail/accounts/{account_id}/sendNot exercised hereSend or save-as-draft a message from a mailbox.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Request body (required) — app__services__salesshift__webmail_router__SendIn

FieldTypeRequiredNotes
tostring[]required
ccstring[]optionalDefault [].
bccstring[]optionalDefault [].
subjectstringoptionalDefault "".
body_htmlstringoptionalDefault "".
attachmentsobject[]optionalDefault [].
modestringoptionalDefault "send".
send_atstring(date-time)?optional
in_reply_tostring?optional
referencesstring?optional
forward_attachmentsobject[]optionalDefault [].
draft_uidinteger?optional
append_signaturebooleanoptionalDefault true.

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "draft_uid": "…",
  "mode": "…",
  "replaced_uid": "…",
  "send_at": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400At least one recipient required
400send_at required for scheduled send
#post-api-v1-salesshift-webmail-accounts-account-id-sendwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:2306Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
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.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
account_idstring(uuid)required

Query parameters

NameTypeRequiredNotes
folderstringrequired
uidintegerrequired

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/accounts/{account_id}/thread?folder=<string>&uid=<integer>' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "count": "…",
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Message not found
#get-api-v1-salesshift-webmail-accounts-account-id-threadwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1716Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
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.

Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Request body (required) — ProvisionIn

FieldTypeRequiredNotes
local_partstringrequired≤ 64 chars.
domainstringrequired
display_namestringoptionalDefault "".
passwordstringrequired
is_sharedbooleanoptionalDefault true.

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400Domain must be one of: {...}
400Invalid mailbox name
409Mailbox already connected
502Mailbox provisioning failed: {...}
#post-api-v1-salesshift-webmail-accounts-provisionwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1229Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
POST/api/v1/salesshift/webmail/ai/improvePartialRewrite a draft body with the org's AI provider at a chosen tone, language and length.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Request body (required) — ImproveIn

FieldTypeRequiredNotes
textstringrequired
tonestringoptionalDefault "friendly".
languagestringoptionalDefault "English".
lengthstringoptionalDefault "medium".

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#post-api-v1-salesshift-webmail-ai-improvewebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:2650The route is implemented and reachable, but it calls the organization's AI integration and the test org has no integration_type='ai' row, so no provider-backed result was produced here.
POST/api/v1/salesshift/webmail/ai/summarize-unreadPartialSummarise up to 15 unread messages with the org's AI provider.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Request body (required) — SummarizeIn

FieldTypeRequiredNotes
account_idstring(uuid)required
folderstringoptionalDefault "INBOX".

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#post-api-v1-salesshift-webmail-ai-summarize-unreadwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:2607The route is implemented and reachable, but it calls the organization's AI integration and the test org has no integration_type='ai' row, so no provider-backed result was produced here.
GET/api/v1/salesshift/webmail/categoriesCalled liveThe organization's webmail categories, seeding the defaults on first use.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/categories' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Observed shape
{
  "success": "bool",
  "data": [
    {
      "id": "str",
      "name": "str",
      "color": "str"
    },
    "...x5"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-webmail-categorieswebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:2520Called against http://127.0.0.1:8741 on 2026-08-06 with a real JWT; the response matched the shape recorded below.
POST/api/v1/salesshift/webmail/categoriesNot exercised hereCreate a webmail category.
Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Request body (required) — CategoryIn

FieldTypeRequiredNotes
namestringrequired
colorstringoptionalDefault "#6b7280".

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
409Category already exists
#post-api-v1-salesshift-webmail-categorieswebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:2539Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
DELETE/api/v1/salesshift/webmail/categories/{category_id}Not exercised hereDelete a webmail category.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
category_idstring(uuid)required

Request

curl
curl -X DELETE 'https://api.vxcloud.io/api/v1/salesshift/webmail/categories/{category_id}' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "success": "…"
}
Top-level keys read from the handler's return statements.
#delete-api-v1-salesshift-webmail-categories-category-idwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:2556Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
POST/api/v1/salesshift/webmail/oauth/{provider}/connectKnown unprovenStart connecting a mailbox — returns the URL to send the user to.
Known unproven. OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
providerstringrequired

Request body (optional) — OAuthConnectIn

FieldTypeRequiredNotes
return_tostringoptionalDefault "/mail".

Values in the request below are typed placeholders, not sample data.

Request

curl
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

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400computed at runtime: str(exc)
#post-api-v1-salesshift-webmail-oauth-provider-connectwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1485OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.
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.

Known unproven. OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.
Auth
no dependency
Scope
url-secret-only
Success
200 · application/json
Envelope
redirect

The provider redirects here with code + state; the state value is what ties the callback to the request that started it.

Query parameters

NameTypeRequiredNotes
codestring?optional
statestring?optional
errorstring?optional
error_descriptionstring?optional

Request

curl
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-callbackwebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1549OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.
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.

Known unproven. OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/webmail/oauth/providers' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Observed shape
{
  "success": "bool",
  "data": [
    {
      "provider": "str",
      "label": "str",
      "configured": "bool",
      "missing": [
        "<empty list>"
      ],
      "scopes": [
        "...",
        "...x6"
      ],
      "redirect_uri": "str",
      "imap_host": "str",
      "smtp_host": "str"
    },
    "...x2"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-webmail-oauth-providerswebmail_router · vxcloud_fastapiclient/app/services/salesshift/webmail_router.py:1474OAuth mailbox connect is fully built — PKCE, Vault-stored tokens, XOAUTH2 and refresh — and a real send completes AUTH XOAUTH2 -> 250 queued. No Microsoft or Google mailbox has ever consented here, so the final SMTP hop to a real provider is UNPROVEN.