TropMail API
v1.0.0https://api.tropmail.com/api/v1Production serverPrivate inbound email API for TropMail.
Create addresses that only receive mail. List, search, read, and update messages; scan and download attachments. Official SDKs and MCP use the same contract.
Base URL
- Production:
https://api.tropmail.com/api/v1
Authentication
API keys from the TropMail dashboard (Pro and above). Keys are 32 alphanumeric characters, optionally prefixed with tm_live_. Send Authorization: Bearer <API_KEY>.
Rate limiting
Per account by plan (Pro 3 / Ultimate 10 / Enterprise 50 requests per second) — not per mailbox or per key. Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. On 429, honor Retry-After.
Response envelope
JSON responses use {success,message,data,error}. success is true and error is null on 200; on errors success is false, data is null, and message/error explain what happened. Correlate with X-Request-ID.
MCP tools receive the unwrapped data object, not this envelope. Attachment download routes return raw bytes, not JSON.
Authentication
BearerAuthhttpTropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
Scheme: bearer (API_KEY)
Health
Health check endpoints
Health check
Service status check. Returns service version and timestamp. No authentication required.
Response
Service is healthy
curl -sS 'https://api.tropmail.com/api/v1/health'import { TropMail } from "@tropmail/sdk";
const client = new TropMail();
const health = await client.health();
console.log(health.status);from tropmail import TropMail
with TropMail() as client:
print(client.health().status)package main
import (
"context"
"fmt"
"log"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New("")
if err != nil {
log.Fatal(err)
}
h, err := client.Health(context.Background())
if err != nil {
log.Fatal(err)
}
fmt.Println(h.Status)
}{
"success": true,
"message": "string",
"data": {},
"error": "string"
}Mailbox
List inboxes at GET /mailboxes. Mail, search, and attachments use /mailbox/{id}/….
List mailboxes
Inboxes this API key may see. data.mailboxes is an array of inbox summaries (it may be empty). Use an id from that list on /mailbox/{id} routes.
Response
List retrieved. data.mailboxes is an array of inbox summaries (may be empty).
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailboxes' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const { mailboxes } = await client.mailboxes.list();
console.log(mailboxes[0]?.email);from tropmail import TropMail
with TropMail() as client:
boxes = client.mailboxes.list()
print(boxes[0].email)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
listed, err := client.Mailboxes.List(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(listed.Mailboxes[0].Email)
}{
"success": true,
"message": "Mailboxes retrieved successfully",
"data": {
"mailboxes": [
{
"id": "11111111-1111-1111-1111-111111111111",
"email": "quiet-otter-1423@tropmail.com",
"opened_count": 10,
"closed_count": 5,
"favorite_count": 2
}
]
},
"error": null
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Get mailbox summary
Summary for one mailbox (opened / closed / favorite counts). Unknown or out-of-scope {id} → 404.
Parameters
idstring<uuid>requiredpathMailbox UUID
Response
One mailbox summary in data (not wrapped in mailboxes).
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const mailbox = await client.mailboxes.get(mailboxId);
console.log(mailbox.email, mailbox.opened_count);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
mailbox = client.mailboxes.get(mailbox_id)
print(mailbox.email, mailbox.opened_count)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
mailbox, err := client.Mailboxes.Get(ctx, mailboxID)
if err != nil {
log.Fatal(err)
}
fmt.Println(mailbox.Email, mailbox.OpenedCount)
}{
"success": true,
"message": "string",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"opened_count": 0,
"closed_count": 0,
"favorite_count": 0
},
"error": "string"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Emails
Email listing, details, and status updates
List emails
Fetch inbox list with pagination and status filtering.
Standard success envelope.
Parameters
idstring<uuid>requiredpathMailbox UUID
limitinteger[1, 100]10querypageinteger>= 11querystatusstringallOpenCloseFavoriteBlockPhishingScamMaliciousallqueryResponse
Emails retrieved successfully
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/emails?limit=10&page=1&status=Open' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const page = await client.emails.list({ mailboxId, limit: 10, page: 1, status: "Open" });
console.log(page.emails[0]?.subject);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
page = client.emails.list(mailbox_id=mailbox_id, limit=10, page=1, status="Open")
print(page.emails[0].subject)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
page, err := client.Emails.List(ctx, tropmail.ListOptions{MailboxID: mailboxID, Limit: 10, Page: 1, Status: "Open"})
if err != nil {
log.Fatal(err)
}
fmt.Println(page.Emails[0].Subject)
}{
"success": true,
"message": "string",
"data": {
"emails": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T09:30:00Z",
"subject": "string",
"from": {
"name": "string",
"address": "user@example.com"
},
"attachmentsCount": 0,
"email_state": "Open",
"action_status": "Favorite",
"status": "string",
"preview": "string"
}
],
"limit": 0,
"page": 0,
"total": 0
}
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Search emails
Full-text search across the authenticated mailbox.
Requires a non-empty query. Pagination mirrors list emails (limit 1-100, page >= 1).
Parameters
idstring<uuid>requiredpathMailbox UUID
querystringrequiredquerylimitinteger[1, 100]10querypageinteger>= 11queryResponse
Search completed successfully
Invalid request — missing search query, malformed UUID, or limit/page out of range. Envelope with success: false.
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/emails/search?query=verification&limit=10&page=1' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const page = await client.emails.search({ mailboxId, query: "verification", limit: 10 });
console.log(page.emails[0]?.subject);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
page = client.emails.search(mailbox_id=mailbox_id, query="verification", limit=10)
print(page.emails[0].subject)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
page, err := client.Emails.Search(ctx, "verification", tropmail.ListOptions{MailboxID: mailboxID, Limit: 10})
if err != nil {
log.Fatal(err)
}
fmt.Println(page.Emails[0].Subject)
}{
"success": true,
"message": "string",
"data": {
"emails": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T09:30:00Z",
"subject": "string",
"from": {
"name": "string",
"address": "user@example.com"
},
"attachmentsCount": 0,
"email_state": "Open",
"action_status": "Favorite",
"status": "string",
"preview": "string"
}
],
"limit": 0,
"page": 0
},
"error": "string"
}{
"success": false,
"message": "Missing or invalid query",
"data": null,
"error": "Missing or invalid query"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Get email detail
Retrieve full email details including content, headers, and attachments. Defaults to HTML view if no view specified.
Parameters
idstring<uuid>requiredpathMailbox UUID
emailIdstring<uuid>requiredpathEmail UUID
timestampstring<date-time>queryOptional email timestamp from list/detail (speeds up lookup)
Response
Email retrieved successfully
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/emails/550e8400-e29b-41d4-a716-446655440001' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const emailId = "550e8400-e29b-41d4-a716-446655440001";
const detail = await client.emails.get(mailboxId, emailId);
console.log(detail.subject);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
detail = client.emails.get(mailbox_id, "550e8400-e29b-41d4-a716-446655440001")
print(detail.subject)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
detail, err := client.Emails.Get(ctx, mailboxID, "550e8400-e29b-41d4-a716-446655440001", tropmail.GetOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Println(detail.Subject)
}
{
"success": true,
"message": "string",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T09:30:00Z",
"subject": "string",
"from": {
"name": "string",
"address": "user@example.com"
},
"attachmentsCount": 0,
"email_state": "Open",
"action_status": "Favorite",
"status": "string",
"preview": "string",
"to": [
{
"name": "string",
"address": "user@example.com"
}
],
"cc": [
{
"name": "string",
"address": "user@example.com"
}
],
"content": "string",
"attachments": [
{
"attachment_id": "550e8400-e29b-41d4-a716-446655440000",
"email_id": "550e8400-e29b-41d4-a716-446655440000",
"filename": "string",
"size": 0,
"mime_type": "string",
"content_id": "string",
"disposition": "inline",
"scan_status": "NotScanned",
"scan_result": {
"status": "NotScanned",
"scannedAt": "2024-01-15T09:30:00Z",
"stats": {
"harmless": 0,
"malicious": 0,
"suspicious": 0,
"undetected": 0
},
"engines": [
{
"engine": "string",
"category": "harmless",
"result": "string"
}
],
"hashes": {
"sha256": "string",
"sha1": "string",
"md5": "string"
},
"error": "string"
},
"scanned_at": "2024-01-15T09:30:00Z"
}
],
"parts": {
"listUnsubscribe": {
"https": "string",
"mailto": "string",
"oneClick": true
},
"calendar": {
"method": "REQUEST",
"uid": "string",
"path": "string"
},
"inlineCids": {}
},
"headers": {},
"security": {}
}
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Update email actions
Update email_state (Open/Close) or action_status (Favorite, Delete, Block, etc.).
- Provide at least one field in the body.
- Optional
timestamp(RFC3339) from list/detail speeds up the update. Blockmarks the message and blocks that sender from future delivery to the mailbox.- To clear
action_status, send an empty string""(also unblocks the sender if it was Blocked).
Body
email_statestringOpenCloseOpen or Close
action_statusstringFavoriteDeleteBlockPhishingScamMaliciousApply action. Empty string clears status.
timestampstring<date-time>Optional email timestamp from list/detail (speeds up lookup)
Parameters
idstring<uuid>requiredpathMailbox UUID
emailIdstring<uuid>requiredpathEmail UUID
Response
Email updated successfully
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X POST 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/emails/550e8400-e29b-41d4-a716-446655440001' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"email_state":"Open","action_status":"Favorite"}'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const emailId = "550e8400-e29b-41d4-a716-446655440001";
await client.emails.update(mailboxId, emailId, {
emailState: "Open",
actionStatus: "Favorite",
});from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
client.emails.update(mailbox_id, "550e8400-e29b-41d4-a716-446655440001", email_state="Open", action_status="Favorite")package main
import (
"context"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
status := tropmail.ActionFavorite
_, err = client.Emails.Update(ctx, mailboxID, "550e8400-e29b-41d4-a716-446655440001", tropmail.UpdateOptions{
EmailState: tropmail.StateOpen,
ActionStatus: &status,
})
if err != nil {
log.Fatal(err)
}
}
{
"email_state": "Open",
"action_status": "Favorite",
"timestamp": "2024-01-15T09:30:00Z"
}{
"success": true,
"message": "string",
"data": {},
"error": "string"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Get email detail in specific view
Retrieve email details with content rendered in a specific format (text, html, markdown).
For markdown: returns markdown immediately when already available. If not, this request waits until conversion finishes (up to about 60 seconds). Timeout is 504; retry the same GET.
Parameters
idstring<uuid>requiredpathMailbox UUID
emailIdstring<uuid>requiredpathEmail UUID
viewstringtexthtmlmarkdownrequiredpathContent format: text, html, or markdown
timestampstring<date-time>queryOptional email timestamp from list/detail (speeds up lookup)
Response
Email retrieved successfully
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/emails/550e8400-e29b-41d4-a716-446655440001/text' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const emailId = "550e8400-e29b-41d4-a716-446655440001";
const detail = await client.emails.get(mailboxId, emailId, { view: "text" });
console.log(detail.content);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
detail = client.emails.get(mailbox_id, "550e8400-e29b-41d4-a716-446655440001", view="text")
print(detail.content)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
detail, err := client.Emails.Get(ctx, mailboxID, "550e8400-e29b-41d4-a716-446655440001", tropmail.GetOptions{View: "text"})
if err != nil {
log.Fatal(err)
}
fmt.Println(detail.Content)
}{
"success": true,
"message": "string",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T09:30:00Z",
"subject": "string",
"from": {
"name": "string",
"address": "user@example.com"
},
"attachmentsCount": 0,
"email_state": "Open",
"action_status": "Favorite",
"status": "string",
"preview": "string",
"to": [
{
"name": "string",
"address": "user@example.com"
}
],
"cc": [
{
"name": "string",
"address": "user@example.com"
}
],
"content": "string",
"attachments": [
{
"attachment_id": "550e8400-e29b-41d4-a716-446655440000",
"email_id": "550e8400-e29b-41d4-a716-446655440000",
"filename": "string",
"size": 0,
"mime_type": "string",
"content_id": "string",
"disposition": "inline",
"scan_status": "NotScanned",
"scan_result": {
"status": "NotScanned",
"scannedAt": "2024-01-15T09:30:00Z",
"stats": {
"harmless": 0,
"malicious": 0,
"suspicious": 0,
"undetected": 0
},
"engines": [
{
"engine": "string",
"category": "harmless",
"result": "string"
}
],
"hashes": {
"sha256": "string",
"sha1": "string",
"md5": "string"
},
"error": "string"
},
"scanned_at": "2024-01-15T09:30:00Z"
}
],
"parts": {
"listUnsubscribe": {
"https": "string",
"mailto": "string",
"oneClick": true
},
"calendar": {
"method": "REQUEST",
"uid": "string",
"path": "string"
},
"inlineCids": {}
},
"headers": {},
"security": {}
}
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Attachments
Attachment metadata, scanning, and downloading
Scan all attachments
Start malware scans for every attachment on an email that is still NotScanned. Each new scan returns Processing right away. Read GET /mailbox/{id}/attachments/{attId} until scan_status is Clean, Malicious, Suspicious, or Unknown. Attachments that already have a result are skipped.
Parameters
idstring<uuid>requiredpathMailbox UUID
emailIdstring<uuid>requiredpathEmail UUID
Response
Scans initiated
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X POST 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/emails/550e8400-e29b-41d4-a716-446655440001/scan-attachments' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const emailId = "550e8400-e29b-41d4-a716-446655440001";
const scans = await client.emails.scanAttachments(mailboxId, emailId);
console.log(scans);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
print(client.emails.scan_attachments(mailbox_id, "550e8400-e29b-41d4-a716-446655440001"))package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
scans, err := client.Emails.ScanAttachments(ctx, mailboxID, "550e8400-e29b-41d4-a716-446655440001")
if err != nil {
log.Fatal(err)
}
fmt.Println(scans)
}{
"success": true,
"message": "string",
"data": {},
"error": "string"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}List attachments for download
List attachments on an email with an available flag (true when the object is stored, including empty files). Fetch bytes via GET /mailbox/{id}/attachments/{attId}/download.
Parameters
idstring<uuid>requiredpathMailbox UUID
emailIdstring<uuid>requiredpathEmail UUID
Response
Download links retrieved
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/emails/550e8400-e29b-41d4-a716-446655440001/download-attachments' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const emailId = "550e8400-e29b-41d4-a716-446655440001";
const files = await client.emails.downloadAttachments(mailboxId, emailId);
console.log(files[0]?.filename);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
files = client.emails.download_attachments(mailbox_id, "550e8400-e29b-41d4-a716-446655440001")
print(files[0].filename)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
files, err := client.Emails.DownloadAttachments(ctx, mailboxID, "550e8400-e29b-41d4-a716-446655440001")
if err != nil {
log.Fatal(err)
}
fmt.Println(files[0].Filename)
}{
"success": true,
"message": "string",
"data": {},
"error": "string"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Get attachment metadata
Read one attachment: filename, size, scan status, and the scan report once the scan has finished.
Parameters
idstring<uuid>requiredpathMailbox UUID
attIdstring<uuid>requiredpathAttachment UUID
Response
Attachment retrieved successfully
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/attachments/660e8400-e29b-41d4-a716-446655440002' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const attachmentId = "660e8400-e29b-41d4-a716-446655440002";
const meta = await client.attachments.get(mailboxId, attachmentId);
console.log(meta.filename, meta.scan_status);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
meta = client.attachments.get(mailbox_id, "660e8400-e29b-41d4-a716-446655440002")
print(meta.filename, meta.scan_status)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
meta, err := client.Attachments.Get(ctx, mailboxID, "660e8400-e29b-41d4-a716-446655440002")
if err != nil {
log.Fatal(err)
}
fmt.Println(meta.Filename, meta.ScanStatus)
}{
"success": true,
"message": "string",
"data": {
"attachment_id": "550e8400-e29b-41d4-a716-446655440000",
"email_id": "550e8400-e29b-41d4-a716-446655440000",
"filename": "string",
"size": 0,
"mime_type": "string",
"content_id": "string",
"disposition": "inline",
"scan_status": "NotScanned",
"scan_result": {
"status": "NotScanned",
"scannedAt": "2024-01-15T09:30:00Z",
"stats": {
"harmless": 0,
"malicious": 0,
"suspicious": 0,
"undetected": 0
},
"engines": [
{
"engine": "string",
"category": "harmless",
"result": "string"
}
],
"hashes": {
"sha256": "string",
"sha1": "string",
"md5": "string"
},
"error": "string"
},
"scanned_at": "2024-01-15T09:30:00Z"
},
"error": "string"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Scan attachment
Start a malware scan for an attachment.
The response is immediate. A new scan comes back as Processing, and scan_result is empty until the scan completes (usually 1–2 minutes). Keep reading GET /mailbox/{id}/attachments/{attId} until scan_status is Clean, Malicious, Suspicious, or Unknown.
- Already
Processing: success, the scan is still running. NotScanned: starts the scan and returnsProcessing.- Already finished: returns the existing status. The file is not scanned again.
Processingfor more than 30 minutes: calling scan again starts a new scan.
Parameters
idstring<uuid>requiredpathMailbox UUID
attIdstring<uuid>requiredpathAttachment UUID
Response
Scan initiated or already in progress
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X POST 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/attachments/660e8400-e29b-41d4-a716-446655440002/scan' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY' \
-H 'Accept: application/json'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const attachmentId = "660e8400-e29b-41d4-a716-446655440002";
await client.attachments.scan(mailboxId, attachmentId);
let info = await client.attachments.get(mailboxId, attachmentId);
while (info.scan_status === "Processing") {
await new Promise((r) => setTimeout(r, 2000));
info = await client.attachments.get(mailboxId, attachmentId);
}
console.log(info.scan_status);from tropmail import TropMail
import time
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
att_id = "660e8400-e29b-41d4-a716-446655440002"
client.attachments.scan(mailbox_id, att_id)
info = client.attachments.get(mailbox_id, att_id)
while info.scan_status == "Processing":
time.sleep(2)
info = client.attachments.get(mailbox_id, att_id)
print(info.scan_status)package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
scan, err := client.Attachments.Scan(ctx, mailboxID, "660e8400-e29b-41d4-a716-446655440002")
if err != nil {
log.Fatal(err)
}
fmt.Println(scan.ScanStatus)
}{
"success": true,
"message": "string",
"data": {},
"error": "string"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Download attachment bytes
Download attachment file bytes. Requires Bearer auth. Response is the raw file body, not a JSON envelope. A file whose size is 0 returns 200 with an empty body. 400 means there is no stored object.
Parameters
idstring<uuid>requiredpathMailbox UUID
attIdstring<uuid>requiredpathAttachment UUID
Response
Attachment file bytes
Attachment exists but has no stored object (empty path). A file whose size is 0 is still downloadable.
Missing, malformed, or unknown Bearer API key. Envelope with success: false.
Plan cannot use the API (Basic/guest) or this key cannot access the mailbox. Envelope with success: false.
Mailbox, email, or attachment UUID unknown or out of this key’s scope. Envelope with success: false.
Account rate limit exceeded (Pro 3/s, Ultimate 10/s, Enterprise 50/s — per account, not per mailbox). Honor Retry-After and X-RateLimit-*.
Authorization
BearerAuthhttp (bearer)TropMail API key from the dashboard. Send Authorization: Bearer <API_KEY>. Required on every /api/v1/* route except GET /health.
curl -sS -X GET 'https://api.tropmail.com/api/v1/mailbox/11111111-1111-1111-1111-111111111111/attachments/660e8400-e29b-41d4-a716-446655440002/download' \
-H 'Authorization: Bearer $TROPMAIL_API_KEY'import { TropMail } from "@tropmail/sdk";
const client = new TropMail({ apiKey: process.env.TROPMAIL_API_KEY! });
const mailboxId = "11111111-1111-1111-1111-111111111111";
const attachmentId = "660e8400-e29b-41d4-a716-446655440002";
const res = await client.attachments.download(mailboxId, attachmentId);
const bytes = await res.arrayBuffer();
console.log(bytes.byteLength);from tropmail import TropMail
with TropMail() as client:
mailbox_id = "11111111-1111-1111-1111-111111111111"
path = client.attachments.download_to(mailbox_id, "660e8400-e29b-41d4-a716-446655440002", "/tmp/attachment.bin")
print(path)
package main
import (
"context"
"fmt"
"log"
"os"
tropmail "github.com/tropmail/tropmail-go"
)
func main() {
client, err := tropmail.New(os.Getenv("TROPMAIL_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
mailboxID := "11111111-1111-1111-1111-111111111111"
n, err := client.Attachments.DownloadTo(ctx, mailboxID, "660e8400-e29b-41d4-a716-446655440002", "/tmp/attachment.bin")
if err != nil {
log.Fatal(err)
}
fmt.Println(n)
}
"<binary>"{
"success": false,
"message": "Attachment unavailable",
"data": null,
"error": "Attachment unavailable"
}{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}{
"success": false,
"message": "API access requires Pro or above",
"data": null,
"error": "Tier not allowed"
}{
"success": false,
"message": "Mailbox not found",
"data": null,
"error": "Mailbox not found"
}{
"success": false,
"message": "Rate limit exceeded",
"data": null,
"error": "Rate limit exceeded"
}Models
StandardResponse
objectStandard JSON envelope. On success error is null and data holds the payload. On failure success is false, data is null, and message / error explain the problem.
successbooleantrue for successful requests, false for errors.
messagestringHuman-readable summary for UI toasts/logging.
dataobjectThe actual payload for successful responses, or null on errors.
errorstring | nullError message string if success is false.
{
"success": true,
"message": "string",
"data": {},
"error": "string"
}EmailListItem
objectidstring<uuid>Email UUID
timestampstring<date-time>Email timestamp (RFC3339)
subjectstringEmail subject
fromobjectattachmentsCountintegerNumber of attachments
email_statestringOpenCloseOpen or Close
action_statusstring | nullFavoriteBlockPhishingScamMaliciousnullFavorite, Block, Phishing, Scam, Malicious, or null
statusstringConvenience field for UI. If action_status is set, matches that; otherwise matches email_state.
previewstringFirst ~100 characters of plain text
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T09:30:00Z",
"subject": "string",
"from": {
"name": "string",
"address": "user@example.com"
},
"attachmentsCount": 0,
"email_state": "Open",
"action_status": "Favorite",
"status": "string",
"preview": "string"
}EmailDetail
objectidstring<uuid>Email UUID
timestampstring<date-time>Email timestamp (RFC3339)
subjectstringEmail subject
fromobjectattachmentsCountintegerNumber of attachments
email_statestringOpenCloseOpen or Close
action_statusstring | nullFavoriteBlockPhishingScamMaliciousnullFavorite, Block, Phishing, Scam, Malicious, or null
statusstringConvenience field for UI. If action_status is set, matches that; otherwise matches email_state.
previewstringFirst ~100 characters of plain text
toArray<object>ccArray<object>contentstringBody content in the requested view (text/html/markdown).
attachmentsArray<Attachment>partsEmailPartsOptional extras from inbound (unsubscribe, calendar, inline images). Omitted when empty.
headersobjectParsed email headers
securityobjectSecurity analysis results (SPF/DKIM/DMARC/spam score)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T09:30:00Z",
"subject": "string",
"from": {
"name": "string",
"address": "user@example.com"
},
"attachmentsCount": 0,
"email_state": "Open",
"action_status": "Favorite",
"status": "string",
"preview": "string",
"to": [
{
"name": "string",
"address": "user@example.com"
}
],
"cc": [
{
"name": "string",
"address": "user@example.com"
}
],
"content": "string",
"attachments": [
{
"attachment_id": "550e8400-e29b-41d4-a716-446655440000",
"email_id": "550e8400-e29b-41d4-a716-446655440000",
"filename": "string",
"size": 0,
"mime_type": "string",
"content_id": "string",
"disposition": "inline",
"scan_status": "NotScanned",
"scan_result": {
"status": "NotScanned",
"scannedAt": "2024-01-15T09:30:00Z",
"stats": {
"harmless": 0,
"malicious": 0,
"suspicious": 0,
"undetected": 0
},
"engines": [
{
"engine": "string",
"category": "harmless",
"result": "string"
}
],
"hashes": {
"sha256": "string",
"sha1": "string",
"md5": "string"
},
"error": "string"
},
"scanned_at": "2024-01-15T09:30:00Z"
}
],
"parts": {
"listUnsubscribe": {
"https": "string",
"mailto": "string",
"oneClick": true
},
"calendar": {
"method": "REQUEST",
"uid": "string",
"path": "string"
},
"inlineCids": {}
},
"headers": {},
"security": {}
}EmailParts
objectOptional MIME extras written when the message arrived.
listUnsubscribeobjectcalendarobjectinlineCidsobjectContent-ID → attachment_id. Use to resolve cid: images.
{
"listUnsubscribe": {
"https": "string",
"mailto": "string",
"oneClick": true
},
"calendar": {
"method": "REQUEST",
"uid": "string",
"path": "string"
},
"inlineCids": {}
}Attachment
objectattachment_idstring<uuid>email_idstring<uuid>filenamestringsizeinteger>= 0Size in bytes. 0 is a real empty file and can be downloaded.
mime_typestringcontent_idstring | nullMIME Content-ID when present (inline images).
dispositionstring | nullinlineattachmentnullinline or attachment. Null on older mail.
scan_statusstringNotScannedProcessingCleanMaliciousSuspiciousUnknownWhether the file has been scanned. After you start a scan this is Processing until the result is ready.
scan_resultScanReportShaped scan report when a scan has completed (or failed).
scanned_atstring<date-time> | null{
"attachment_id": "550e8400-e29b-41d4-a716-446655440000",
"email_id": "550e8400-e29b-41d4-a716-446655440000",
"filename": "string",
"size": 0,
"mime_type": "string",
"content_id": "string",
"disposition": "inline",
"scan_status": "NotScanned",
"scan_result": {
"status": "NotScanned",
"scannedAt": "2024-01-15T09:30:00Z",
"stats": {
"harmless": 0,
"malicious": 0,
"suspicious": 0,
"undetected": 0
},
"engines": [
{
"engine": "string",
"category": "harmless",
"result": "string"
}
],
"hashes": {
"sha256": "string",
"sha1": "string",
"md5": "string"
},
"error": "string"
},
"scanned_at": "2024-01-15T09:30:00Z"
}MailboxSummary
objectidstring<uuid>emailstring<email>opened_countintegerclosed_countintegerfavorite_countinteger{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"opened_count": 0,
"closed_count": 0,
"favorite_count": 0
}ScanReport
objectMalware scan report for an attachment (engine summaries and optional file hashes).
statusstringNotScannedProcessingCleanMaliciousSuspiciousUnknownrequiredscannedAtstring<date-time>requiredstatsobjectrequiredenginesArray<object>requiredhashesobjecterrorstringMachine-readable failure code when the scan could not complete
{
"status": "NotScanned",
"scannedAt": "2024-01-15T09:30:00Z",
"stats": {
"harmless": 0,
"malicious": 0,
"suspicious": 0,
"undetected": 0
},
"engines": [
{
"engine": "string",
"category": "harmless",
"result": "string"
}
],
"hashes": {
"sha256": "string",
"sha1": "string",
"md5": "string"
},
"error": "string"
}MailboxList
objectInboxes this API key may see. An empty array means this key has no inboxes yet.
mailboxesArray<MailboxSummary>required{
"mailboxes": [
{
"id": "11111111-1111-1111-1111-111111111111",
"email": "quiet-otter-1423@tropmail.com",
"opened_count": 10,
"closed_count": 5,
"favorite_count": 2
}
]
}ErrorEnvelope
objectError envelope. success is false; data is null.
successbooleanrequiredAlways false on errors.
messagestringrequiredHuman-readable summary for UI toasts and logs.
dataany | nullrequiredAlways null on errors.
errorstring | nullrequiredError detail string when success is false.
{
"success": false,
"message": "Unauthorized",
"data": null,
"error": "Missing or invalid API key"
}