SSpiderIQ
SSpiderIQ

Docs / api-reference/spidermail

SpiderMail

35 endpoints from the published OpenAPI import.

GET/api/v1/mail/providers

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

StatusDescription
200Successful Response
GET/api/v1/mail/mailboxes

List Mailboxes

List all mailboxes for the authenticated client (passwords excluded).

Use ?format=yaml for agent-friendly output.

Parameters

NameInTypeRequiredDescription
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/mailboxes

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

StatusDescription
201Successful Response
422Validation Error
GET/api/v1/mail/mailboxes/stats

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

NameInTypeRequiredDescription
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
PATCH/api/v1/mail/mailboxes/{email}

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

NameInTypeRequiredDescription
emailpathstringtrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
DELETE/api/v1/mail/mailboxes/{email}

Delete Mailbox

Delete a mailbox and all its messages.

Parameters

NameInTypeRequiredDescription
emailpathstringtrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/mailboxes/{email}/test

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

NameInTypeRequiredDescription
emailpathstringtrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/folders

Get Folders

Get folder list with message counts for a mailbox.

Parameters

NameInTypeRequiredDescription
emailquerystringtrueMailbox email address
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/inbox

Get Inbox

Get inbox messages.

Three scopes, resolved in priority order:

  1. ?email=alice@... → single mailbox (legacy; byte-identical response).
  2. ?view_id=N → apply saved view's filter_config from /mail/views.
  3. 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

NameInTypeRequiredDescription
emailqueryanyfalseMailbox email address. Omit for Master Inbox (all client mailboxes).
view_idqueryanyfalseApply a saved view's filter_config (see /mail/views). Direct query params override the view's defaults.
limitqueryintegerfalseNumber of messages to return
offsetqueryintegerfalseOffset for pagination
unread_onlyquerybooleanfalseOnly return unread messages
starred_onlyquerybooleanfalseOnly return starred messages
outreach_classificationqueryanyfalseFilter by outreach classification (e.g. 'warmup'). Overrides a saved view's classification.
has_attachmentsqueryanyfalseFilter to messages with (true) / without (false) attachments.
folderqueryanyfalseFolder to filter by (INBOX, Sent, Drafts, Trash)
directionqueryanyfalseDirection filter: inbound or outbound
include_bodyquerybooleanfalseInclude 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.
formatqueryanyfalseOutput format: json or yaml
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/messages/{message_id}

Get Message

Get full message by database ID. Use ?format=yaml for agent-friendly output. Also marks as read.

Parameters

NameInTypeRequiredDescription
message_idpathintegertrue
formatqueryanyfalseOutput format: json or yaml
include_attachmentsquerybooleanfalseInclude attachment summaries in response
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
PATCH/api/v1/mail/messages/{message_id}

Update Message Flags

Update message flags (is_read, is_starred, labels).

Parameters

NameInTypeRequiredDescription
message_idpathintegertrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/threads/{thread_id}

Get Thread

Get all messages in a thread. Use ?format=yaml for agent-friendly output.

Parameters

NameInTypeRequiredDescription
thread_idpathstringtrue
formatqueryanyfalseOutput format: json or yaml
include_attachmentsquerybooleanfalseInclude attachment summaries in response
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/search

Search Messages

Search messages using full-text search and filters.

Parameters

NameInTypeRequiredDescription
emailquerystringtrueMailbox email address
qqueryanyfalseFull-text search query
from_addrqueryanyfalseFilter by sender address
subjectqueryanyfalseFilter by subject (substring match)
sincequeryanyfalseMessages since this date (ISO 8601)
beforequeryanyfalseMessages before this date (ISO 8601)
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/messages/bulk

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/messages/{message_id}/snooze

Snooze Message

Snooze a message until a specific time.

Parameters

NameInTypeRequiredDescription
message_idpathintegertrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
DELETE/api/v1/mail/messages/{message_id}/snooze

Unsnooze Message

Unsnooze a message, returning it to its original folder.

Parameters

NameInTypeRequiredDescription
message_idpathintegertrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/snoozed

List Snoozed Messages

List all snoozed messages for the client.

Parameters

NameInTypeRequiredDescription
emailqueryanyfalseFilter by mailbox email
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/labels

List Labels

List all label definitions for the client.

Parameters

NameInTypeRequiredDescription
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/labels

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

StatusDescription
201Successful Response
422Validation Error
PATCH/api/v1/mail/labels/{label_id}

Update Label

Update a label definition (name and/or color).

Parameters

NameInTypeRequiredDescription
label_idpathintegertrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
DELETE/api/v1/mail/labels/{label_id}

Delete Label

Delete a label definition.

Parameters

NameInTypeRequiredDescription
label_idpathintegertrue
Try it
Query

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

StatusDescription
204Successful Response
422Validation Error
GET/api/v1/mail/templates

List Templates

List all email templates for the client.

Parameters

NameInTypeRequiredDescription
template_typequeryanyfalseFilter by type: signature, header, layout, full
active_onlyquerybooleanfalseOnly return active templates (false also returns soft-deleted ones)
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/templates

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

StatusDescription
201Successful Response
422Validation Error
GET/api/v1/mail/templates/{template_id}

Get Template

Get a single template by ID.

Parameters

NameInTypeRequiredDescription
template_idpathintegertrue
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
PATCH/api/v1/mail/templates/{template_id}

Update Template

Update an existing template.

Parameters

NameInTypeRequiredDescription
template_idpathintegertrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
DELETE/api/v1/mail/templates/{template_id}

Delete Template

Delete a template (soft delete by setting is_active=false).

Parameters

NameInTypeRequiredDescription
template_idpathintegertrue
Try it
Query

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

StatusDescription
204Successful Response
422Validation Error
GET/api/v1/mail/security/events

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

NameInTypeRequiredDescription
emailqueryanyfalseFilter by mailbox email address
event_typequeryanyfalseFilter by event type (injection_detected, credential_blocked, etc.)
limitqueryintegerfalseNumber of events to return
offsetqueryintegerfalseOffset for pagination
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/quarantine

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

NameInTypeRequiredDescription
emailqueryanyfalseFilter by mailbox email address
limitqueryintegerfalseNumber of messages to return
offsetqueryintegerfalseOffset for pagination
formatqueryanyfalseOutput format: json, yaml, or llm
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/messages/{message_id}/release

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

NameInTypeRequiredDescription
message_idpathintegertrue
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/session

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

NameInTypeRequiredDescription
emailquerystringtrueMailbox email address
include_recentqueryintegerfalseNumber of recent messages to include
formatqueryanyfalseOutput format: json or yaml
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
POST/api/v1/mail/compose/assist

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/mailboxes/{email}/analytics

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

NameInTypeRequiredDescription
emailpathstringtrue
daysqueryintegerfalseLook-back window in days.
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/usage-summary

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

NameInTypeRequiredDescription
periodquerystringfalseCalendar-to-date window: week | month | year.
Try it
Query

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

StatusDescription
200Successful Response
422Validation Error
GET/api/v1/mail/agent-access

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

StatusDescription
200Successful Response
POST/api/v1/mail/convert

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

StatusDescription
200Successful Response
422Validation Error