SpiderMail
35 endpoints from the published OpenAPI import.
List Provider Presets
List the supported providers and their published connection defaults.
Static configuration — no database access, no per-tenant content. It is still behind the normal mail read dependency rather than public: the list names our supported integrations, and there is no reason for it to be enumerable anonymously.
Callers use this to render the provider picker and to pre-fill the connection form. Everything returned here is exactly what POST /mailboxes would have filled in for an omitted field, so a caller can equally omit the fields and skip this endpoint entirely — the presets are applied server-side either way.
A preset may also carry routed_providers: named providers that work but are deliberately not presets. Only generic_imap carries any (Fastmail). The list is what a user searching for their own provider finds; leaving it unmapped here would restore exactly the silence card 3.1b exists to end, so it is asserted against this handler's real output, not against the registry.
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/providers' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/providers",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/providers", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/providers", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
List Mailboxes
List all mailboxes for the authenticated client (passwords excluded).
Use ?format=yaml for agent-friendly output.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/mailboxes' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/mailboxes",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/mailboxes", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/mailboxes", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Register Mailbox
Register a new email mailbox. Passwords are encrypted at rest.
Resource-quota check (Plans Initiative F2, migration 248): a client may own at most max_mailboxes mailboxes. NULL = unlimited (the default for every existing client at F2 ship time). 403 returns a structured error="resource_quota_exceeded" body so dashboard / agent callers can surface the cap value directly.
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/mailboxes' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"email_address": "alice@company.com",
"display_name": "string",
"provider": "zoho",
"imap_host": "string",
"imap_port": 0,
"imap_username": "string",
"imap_password": "string",
"smtp_host": "string",
"smtp_port": 0,
"smtp_username": "string",
"smtp_password": "string",
"skip_verification": false,
"sync_scope": "last_n",
"sync_scope_n": 0,
"sync_since_date": "2026-01-01",
"poll_interval_seconds": 0
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/mailboxes",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"email_address": "alice@company.com", "display_name": "string", "provider": "zoho", "imap_host": "string", "imap_port": 0, "imap_username": "string", "imap_password": "string", "smtp_host": "string", "smtp_port": 0, "smtp_username": "string", "smtp_password": "string", "skip_verification": false, "sync_scope": "last_n", "sync_scope_n": 0, "sync_since_date": "2026-01-01", "poll_interval_seconds": 0},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/mailboxes", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"email_address": "alice@company.com", "display_name": "string", "provider": "zoho", "imap_host": "string", "imap_port": 0, "imap_username": "string", "imap_password": "string", "smtp_host": "string", "smtp_port": 0, "smtp_username": "string", "smtp_password": "string", "skip_verification": false, "sync_scope": "last_n", "sync_scope_n": 0, "sync_since_date": "2026-01-01", "poll_interval_seconds": 0})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"email_address": "alice@company.com", "display_name": "string", "provider": "zoho", "imap_host": "string", "imap_port": 0, "imap_username": "string", "imap_password": "string", "smtp_host": "string", "smtp_port": 0, "smtp_username": "string", "smtp_password": "string", "skip_verification": false, "sync_scope": "last_n", "sync_scope_n": 0, "sync_since_date": "2026-01-01", "poll_interval_seconds": 0}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/mailboxes", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
201 | Successful Response |
422 | Validation Error |
Get Mailbox Stats
Per-mailbox aggregates for the Master Inbox sidebar. ?format=yaml supported.
At 200 mailboxes per client the naive pattern — one COUNT query per mailbox from the dashboard — costs 200 round-trips per page load. This endpoint returns the full stats row for every mailbox the client owns in a single aggregate query, plus a totals block so the sidebar header can render client-wide counts without a second call.
Health is the SAME five-state contract GET /mail/mailboxes serves — active / connecting / error / stalled / disabled, plus health_detail, health_since and the classified poll_error_cause / poll_error_action. It is derived by the one shared _apply_health(), not computed here.
🔴 Why this docstring used to describe a DIFFERENT vocabulary ------------------------------------------------------------ Until 2026-08-10 this handler carried its own ladder — ok / warning / stale / disabled — with its own hardcoded one-hour staleness cutoff. Card UI.2 (#3046) derived health properly in services/mail/mailbox_health.py and wired GET /mail/mailboxes, the CLI and the MCP list_mailboxes tool, and did not sweep this function. So the two endpoints disagreed about the same row at the same moment, which is what a marketplace vendor measured and filed:
GET /mail/mailboxes -> health="error" + health_detail GET /mail/mailboxes/stats -> health="warning" + no health_detail (healthy rows: "active" vs "ok")
Two defects in one, and the second is the worse of them. warning was not in the documented state set at all, so an agent branching on the contract had no case for it — and the safe-looking fallback (treat unknown as healthy) is the dangerous one. On top of that the two thresholds differed (1h here vs STALE_POLL_HOURS, 6h, there), so a mailbox could read stale on one endpoint and active on the other purely from the mismatch.
⚠️ The CI guard built to prevent exactly this could not see it. tests/unit/mail/test_mailbox_status_surfaces.py located the region it inspects with src.index("async def get_mailbox_stats") — it used this function as the END BOUNDARY and stopped reading one line before the bug. LEARNINGS §89 counted the ternary "five separate times across four files"; this was the sixth site, in Python, just outside the window. A guard's coverage is defined by where it stops looking, and nothing announces that.
Both surfaces now go through _apply_health(), so there is one definition and adding a state cannot repaint only half the product.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/mailboxes/stats' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/mailboxes/stats",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/mailboxes/stats", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/mailboxes/stats", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Update Mailbox
Update mailbox settings including template defaults.
Supports updating:
- display_name: Display name for From header
- is_active: Enable/disable mailbox polling
- default_template_id: Default template for outbound emails
- template_variables: Default variable values (merged with job-level data)
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | path | string | true |
Try it
Examples
cURL
curl -X PATCH 'https://spideriq.ai/api/v1/mail/mailboxes/{email}' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"default_template_id": 1,
"display_name": "Alice Smith",
"template_variables": {
"company_name": "Acme Corp",
"phone": "+1 555-123-4567",
"sender_name": "Alice Smith",
"title": "Sales Director"
}
}'Python
import httpx
resp = httpx.patch(
"https://spideriq.ai/api/v1/mail/mailboxes/{email}",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"default_template_id": 1, "display_name": "Alice Smith", "template_variables": {"company_name": "Acme Corp", "phone": "+1 555-123-4567", "sender_name": "Alice Smith", "title": "Sales Director"}},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/mailboxes/{email}", {
method: "PATCH",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"default_template_id": 1, "display_name": "Alice Smith", "template_variables": {"company_name": "Acme Corp", "phone": "+1 555-123-4567", "sender_name": "Alice Smith", "title": "Sales Director"}})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"default_template_id": 1, "display_name": "Alice Smith", "template_variables": {"company_name": "Acme Corp", "phone": "+1 555-123-4567", "sender_name": "Alice Smith", "title": "Sales Director"}}`)
req, _ := http.NewRequest("PATCH", "https://spideriq.ai/api/v1/mail/mailboxes/{email}", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Delete Mailbox
Delete a mailbox and all its messages.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | path | string | true |
Try it
Examples
cURL
curl -X DELETE 'https://spideriq.ai/api/v1/mail/mailboxes/{email}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.delete(
"https://spideriq.ai/api/v1/mail/mailboxes/{email}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/mailboxes/{email}", {
method: "DELETE",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://spideriq.ai/api/v1/mail/mailboxes/{email}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Test Mailbox
Test IMAP and SMTP connectivity for a mailbox.
Gated on mail:test, which mail:write and email:admin also satisfy — so every token that could reach this route before still can, and a read-only monitoring agent can now be given the narrow scope instead of the one that can delete mailboxes. See _MAIL_TEST for why this is a third scope rather than a move to mail:read.
⚠️ The probe is AUTH-only (login() on both protocols). Per LEARNINGS §27 a MAIL FROM/RCPT probe returns a false 250 on a send-blocked domain — Zoho rejects at DATA. A green result here is NOT evidence the mailbox can send.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | path | string | true |
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/mailboxes/{email}/test' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/mailboxes/{email}/test",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/mailboxes/{email}/test", {
method: "POST",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/mailboxes/{email}/test", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Folders
Get folder list with message counts for a mailbox.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | query | string | true | Mailbox email address |
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/folders' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/folders",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/folders", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/folders", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Inbox
Get inbox messages.
Three scopes, resolved in priority order:
?email=alice@...→ single mailbox (legacy; byte-identical response).?view_id=N→ apply saved view's filter_config from /mail/views.- neither → Master Inbox (client-wide), streams from the denormalized idx_mail_messages_client_*_date indexes introduced in migration 139.
Direct query params (unread_only, starred_only, outreach_classification, has_attachments, folder, direction) override the saved view's equivalents. Use ?format=yaml for agent-friendly output.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | query | any | false | Mailbox email address. Omit for Master Inbox (all client mailboxes). |
view_id | query | any | false | Apply a saved view's filter_config (see /mail/views). Direct query params override the view's defaults. |
limit | query | integer | false | Number of messages to return |
offset | query | integer | false | Offset for pagination |
unread_only | query | boolean | false | Only return unread messages |
starred_only | query | boolean | false | Only return starred messages |
outreach_classification | query | any | false | Filter by outreach classification (e.g. 'warmup'). Overrides a saved view's classification. |
has_attachments | query | any | false | Filter to messages with (true) / without (false) attachments. |
folder | query | any | false | Folder to filter by (INBOX, Sent, Drafts, Trash) |
direction | query | any | false | Direction filter: inbound or outbound |
include_body | query | boolean | false | Include full body_text/body_html on each message (avoids the N+1 fan-out to /mail/messages/{id}). Off by default for the lightweight preview-only response. |
format | query | any | false | Output format: json or yaml |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/inbox' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/inbox",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/inbox", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/inbox", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Message
Get full message by database ID. Use ?format=yaml for agent-friendly output. Also marks as read.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
message_id | path | integer | true | |
format | query | any | false | Output format: json or yaml |
include_attachments | query | boolean | false | Include attachment summaries in response |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/messages/{message_id}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/messages/{message_id}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/messages/{message_id}", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/messages/{message_id}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Update Message Flags
Update message flags (is_read, is_starred, labels).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
message_id | path | integer | true |
Try it
Examples
cURL
curl -X PATCH 'https://spideriq.ai/api/v1/mail/messages/{message_id}' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"is_read": true,
"is_starred": true,
"labels": [
"string"
],
"notes": "string"
}'Python
import httpx
resp = httpx.patch(
"https://spideriq.ai/api/v1/mail/messages/{message_id}",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"is_read": true, "is_starred": true, "labels": ["string"], "notes": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/messages/{message_id}", {
method: "PATCH",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"is_read": true, "is_starred": true, "labels": ["string"], "notes": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"is_read": true, "is_starred": true, "labels": ["string"], "notes": "string"}`)
req, _ := http.NewRequest("PATCH", "https://spideriq.ai/api/v1/mail/messages/{message_id}", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Thread
Get all messages in a thread. Use ?format=yaml for agent-friendly output.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
thread_id | path | string | true | |
format | query | any | false | Output format: json or yaml |
include_attachments | query | boolean | false | Include attachment summaries in response |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/threads/{thread_id}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/threads/{thread_id}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/threads/{thread_id}", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/threads/{thread_id}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Search Messages
Search messages using full-text search and filters.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | query | string | true | Mailbox email address |
q | query | any | false | Full-text search query |
from_addr | query | any | false | Filter by sender address |
subject | query | any | false | Filter by subject (substring match) |
since | query | any | false | Messages since this date (ISO 8601) |
before | query | any | false | Messages before this date (ISO 8601) |
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/search' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/search",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/search", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/search", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Bulk Update Messages
Bulk update messages: mark_read, mark_unread, archive, delete, add_label.
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/messages/bulk' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"message_ids": [
0
],
"action": "string",
"label": "string"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/messages/bulk",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"message_ids": [0], "action": "string", "label": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/messages/bulk", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"message_ids": [0], "action": "string", "label": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"message_ids": [0], "action": "string", "label": "string"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/messages/bulk", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Snooze Message
Snooze a message until a specific time.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
message_id | path | integer | true |
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"snoozed_until": "2026-01-01T00:00:00Z"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"snoozed_until": "2026-01-01T00:00:00Z"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"snoozed_until": "2026-01-01T00:00:00Z"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"snoozed_until": "2026-01-01T00:00:00Z"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Unsnooze Message
Unsnooze a message, returning it to its original folder.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
message_id | path | integer | true |
Try it
Examples
cURL
curl -X DELETE 'https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.delete(
"https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze", {
method: "DELETE",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://spideriq.ai/api/v1/mail/messages/{message_id}/snooze", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
List Snoozed Messages
List all snoozed messages for the client.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | query | any | false | Filter by mailbox email |
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/snoozed' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/snoozed",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/snoozed", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/snoozed", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
List Labels
List all label definitions for the client.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/labels' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/labels",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/labels", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/labels", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Create Label
Create a new label definition.
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/labels' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "string",
"color": "#6B7280"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/labels",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"name": "string", "color": "#6B7280"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/labels", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"name": "string", "color": "#6B7280"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"name": "string", "color": "#6B7280"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/labels", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
201 | Successful Response |
422 | Validation Error |
Update Label
Update a label definition (name and/or color).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
label_id | path | integer | true |
Try it
Examples
cURL
curl -X PATCH 'https://spideriq.ai/api/v1/mail/labels/{label_id}' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "string",
"color": "string"
}'Python
import httpx
resp = httpx.patch(
"https://spideriq.ai/api/v1/mail/labels/{label_id}",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"name": "string", "color": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/labels/{label_id}", {
method: "PATCH",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"name": "string", "color": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"name": "string", "color": "string"}`)
req, _ := http.NewRequest("PATCH", "https://spideriq.ai/api/v1/mail/labels/{label_id}", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Delete Label
Delete a label definition.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
label_id | path | integer | true |
Try it
Examples
cURL
curl -X DELETE 'https://spideriq.ai/api/v1/mail/labels/{label_id}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.delete(
"https://spideriq.ai/api/v1/mail/labels/{label_id}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/labels/{label_id}", {
method: "DELETE",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://spideriq.ai/api/v1/mail/labels/{label_id}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
204 | Successful Response |
422 | Validation Error |
List Templates
List all email templates for the client.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
template_type | query | any | false | Filter by type: signature, header, layout, full |
active_only | query | boolean | false | Only return active templates (false also returns soft-deleted ones) |
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/templates' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/templates",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/templates", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/templates", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Create Template
Create a new email template.
Note on SQL: use CAST(:variables AS jsonb) for the JSONB column cast. The shorter Postgres ::jsonb syntax does NOT work here because SQLAlchemy tokenizes :variables::jsonb as bind-parameter :variables followed by literal ::jsonb, producing a PostgresSyntaxError at execute time. The mail_templates.py sibling endpoint uses the correct form (app/api/v1/mail_templates.py:92) and has always worked; this one shipped broken in v2.52.0 and was masked by an over-broad except Exception → 409 that blamed every failure on name conflicts. Surfaced 2026-04-21 during the Stage 6 E2E test sweep (PR #X).
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/templates' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"description": "Welcome email for new contacts",
"html_source": "<html><body><h1>Welcome {{ name }}!</h1><p>{{ body }}</p></body></html>",
"name": "welcome-email",
"template_type": "full",
"variables": [
"name",
"body"
]
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/templates",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"description": "Welcome email for new contacts", "html_source": "<html><body><h1>Welcome {{ name }}!</h1><p>{{ body }}</p></body></html>", "name": "welcome-email", "template_type": "full", "variables": ["name", "body"]},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/templates", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"description": "Welcome email for new contacts", "html_source": "<html><body><h1>Welcome {{ name }}!</h1><p>{{ body }}</p></body></html>", "name": "welcome-email", "template_type": "full", "variables": ["name", "body"]})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"description": "Welcome email for new contacts", "html_source": "<html><body><h1>Welcome {{ name }}!</h1><p>{{ body }}</p></body></html>", "name": "welcome-email", "template_type": "full", "variables": ["name", "body"]}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/templates", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
201 | Successful Response |
422 | Validation Error |
Get Template
Get a single template by ID.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
template_id | path | integer | true | |
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/templates/{template_id}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/templates/{template_id}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/templates/{template_id}", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/templates/{template_id}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Update Template
Update an existing template.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
template_id | path | integer | true |
Try it
Examples
cURL
curl -X PATCH 'https://spideriq.ai/api/v1/mail/templates/{template_id}' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "string",
"description": "string",
"template_type": "signature",
"html_source": "string",
"text_source": "string",
"variables": [
"string"
],
"is_active": true,
"is_default": true
}'Python
import httpx
resp = httpx.patch(
"https://spideriq.ai/api/v1/mail/templates/{template_id}",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"name": "string", "description": "string", "template_type": "signature", "html_source": "string", "text_source": "string", "variables": ["string"], "is_active": true, "is_default": true},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/templates/{template_id}", {
method: "PATCH",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"name": "string", "description": "string", "template_type": "signature", "html_source": "string", "text_source": "string", "variables": ["string"], "is_active": true, "is_default": true})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"name": "string", "description": "string", "template_type": "signature", "html_source": "string", "text_source": "string", "variables": ["string"], "is_active": true, "is_default": true}`)
req, _ := http.NewRequest("PATCH", "https://spideriq.ai/api/v1/mail/templates/{template_id}", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Delete Template
Delete a template (soft delete by setting is_active=false).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
template_id | path | integer | true |
Try it
Examples
cURL
curl -X DELETE 'https://spideriq.ai/api/v1/mail/templates/{template_id}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.delete(
"https://spideriq.ai/api/v1/mail/templates/{template_id}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/templates/{template_id}", {
method: "DELETE",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://spideriq.ai/api/v1/mail/templates/{template_id}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
204 | Successful Response |
422 | Validation Error |
List Security Events
List security events for this client's mailboxes.
Security events are logged when:
- Prompt injection patterns are detected in inbound emails
- Credential leaks are blocked in outbound emails
- Messages are quarantined or released
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | query | any | false | Filter by mailbox email address |
event_type | query | any | false | Filter by event type (injection_detected, credential_blocked, etc.) |
limit | query | integer | false | Number of events to return |
offset | query | integer | false | Offset for pagination |
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/security/events' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/security/events",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/security/events", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/security/events", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
List Quarantined Messages
List quarantined messages for this client's mailboxes.
Quarantined messages are inbound emails flagged with potential prompt injection attacks. They are stored but marked for admin review.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | query | any | false | Filter by mailbox email address |
limit | query | integer | false | Number of messages to return |
offset | query | integer | false | Offset for pagination |
format | query | any | false | Output format: json, yaml, or llm |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/quarantine' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/quarantine",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/quarantine", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/quarantine", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Release From Quarantine
Release a message from quarantine after admin review.
This marks the message as safe and logs a security event. The message will then be visible to agents.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
message_id | path | integer | true |
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/messages/{message_id}/release' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/messages/{message_id}/release",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/messages/{message_id}/release", {
method: "POST",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/messages/{message_id}/release", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Session
Get session context for AI agent bootstrap.
Returns mailbox info, unread count, and recent messages in a single call. This is optimized for agents that need to quickly understand mailbox state.
Use ?format=yaml for token-efficient agent consumption (~60% token savings).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | query | string | true | Mailbox email address |
include_recent | query | integer | false | Number of recent messages to include |
format | query | any | false | Output format: json or yaml |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/session' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/session",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/session", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/session", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Compose Assist
AI-powered email composition assistant.
Actions:
- write: Generate new email from subject
- rewrite: Improve existing content
- expand: Make content longer
- shorten: Condense to key points
- formal: Make more professional
- casual: Make more relaxed
- fix_grammar: Correct errors
Uses LiteLLM proxy for model routing.
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/compose/assist' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"action": "write",
"context": "",
"subject": "string",
"tone": "professional",
"thread_context": "string"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/compose/assist",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"action": "write", "context": "", "subject": "string", "tone": "professional", "thread_context": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/compose/assist", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"action": "write", "context": "", "subject": "string", "tone": "professional", "thread_context": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"action": "write", "context": "", "subject": "string", "tone": "professional", "thread_context": "string"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/compose/assist", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Mailbox Analytics
Per-mailbox message analytics for the inbox sidebar (DEMOCK-PLAN #13).
Returns totals (sent / received / replies / unread) and a by_day inbound-vs-outbound time-series over the last days. Warmup traffic is excluded throughout (mirrors the inbox default) so the numbers reflect real mail, not Smartlead warmup noise. replies = inbound messages with an In-Reply-To header (a subset of received). Uses the existing (mailbox_id, date) index; both aggregates are single table scans.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
email | path | string | true | |
days | query | integer | false | Look-back window in days. |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/mailboxes/{email}/analytics' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/mailboxes/{email}/analytics",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/mailboxes/{email}/analytics", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/mailboxes/{email}/analytics", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Usage Summary
AI-usage KPI card (DEMOCK-PLAN #5 / D1 — "AI usage this month").
emails_handled is a real mail_messages count for this client, calendar period-to-date, warmup excluded. ai_tokens / est_cost_usd are returned null with ai_metering_available=false: /compose/assist calls the raw litellm proxy with a shared master key, so its usage is not attributable to a client in gate_request_logs. Surfacing real AI tokens requires routing compose/assist through SpiderGate V2 — a deferred metering slice.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
period | query | string | false | Calendar-to-date window: week | month | year. |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/usage-summary' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/usage-summary",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/usage-summary", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/usage-summary", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Agent Access
Client-wide agent-token list for the Agent Access table (DEMOCK-PLAN #19 / D2).
Lists this client's active (non-revoked) agent_tokens with owner email resolved from agent_users. agent_tokens.client_id stores the cli_xxx slug, so this keys on client.slug (NOT the UUID). Per-mailbox columns are intentionally omitted — tokens are client-scoped with no mailbox link.
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/mail/agent-access' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/mail/agent-access",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/agent-access", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/mail/agent-access", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
Convert a mail body between Markdown and HTML
Convert content between Markdown and HTML.
Synchronous and CPU-bound (~1 ms/KB measured), so the work runs in asyncio.to_thread and never blocks the event loop — CLAUDE.md Rule 1, the same hazard class as the SSE busy-loop incident in docs/services/fastapi/LEARNINGS.md.
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/mail/convert' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"content": "# Hi\n\nSee the **report**.",
"from": "markdown",
"mode": "fit",
"to": "html"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/mail/convert",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"content": "# Hi\n\nSee the **report**.", "from": "markdown", "mode": "fit", "to": "html"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/mail/convert", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"content": "# Hi\n\nSee the **report**.", "from": "markdown", "mode": "fit", "to": "html"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"content": "# Hi\n\nSee the **report**.", "from": "markdown", "mode": "fit", "to": "html"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/mail/convert", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |