# Mail Developer API - Complete Specification (v1.60) > Complete API reference for AI agents, LLMs, and client integrations at dev.txnal.com. ## Base URLs - Production: `https://mail.txnal.com` - Developer Portal: `https://dev.txnal.com` - Local Development: `http://localhost:3000` ## Authentication & Security The Mail API implements a two-tier token hierarchy designed for client applications and AI agents: 1. **Refresh Token (`type: refresh`)**: Valid for 7 days. Stored securely and exchanged for access tokens. 2. **Access Token (`type: access`)**: Valid for 5 minutes. Transmitted in HTTP requests: `Authorization: Bearer ` 3. When an access token expires (HTTP 401 `Token has expired`), call `POST /api/v1/auth/token` with the refresh token to obtain a fresh access token and a rotated refresh token. --- ### POST `/api/v1/auth/token` **Summary**: Mint or rotate access token Exchanges an active refresh token for a newly minted 5-minute access token and a rotated 7-day refresh token. Implements standard OAuth 2.0 refresh_token grant. - **Tags**: Authentication - **Operation ID**: `createAuthToken` - **Security**: Public #### Request Body Schema: `AuthTokenRequest` | Field | Type | Description | | --- | --- | --- | | `grant_type` | string | Example: `refresh_token` | | `refresh_token` | string | JWT refresh token previously minted by client session | #### Responses - **HTTP 200**: Token successfully minted and rotated - Schema: `AuthTokenResponse` - **HTTP 400**: Invalid request or unsupported grant type - Schema: `ErrorResponse` - **HTTP 401**: Invalid, expired, or revoked refresh token - Schema: `ErrorResponse` --- ### POST `/api/v1/auth/tokens` **Summary**: Mint or rotate access token (alias) Backward-compatible alias for `/api/v1/auth/token`. - **Tags**: Authentication - **Operation ID**: `createAuthTokensAlias` - **Security**: Public #### Request Body Schema: `AuthTokenRequest` | Field | Type | Description | | --- | --- | --- | | `grant_type` | string | Example: `refresh_token` | | `refresh_token` | string | JWT refresh token previously minted by client session | #### Responses - **HTTP 200**: Token successfully minted and rotated - Schema: `AuthTokenResponse` --- ### POST `/api/v1/auth/rotate` **Summary**: Rotate refresh and access token (alias) Backward-compatible alias for `/api/v1/auth/token`. - **Tags**: Authentication - **Operation ID**: `rotateAuthTokenAlias` - **Security**: Public #### Request Body Schema: `AuthTokenRequest` | Field | Type | Description | | --- | --- | --- | | `grant_type` | string | Example: `refresh_token` | | `refresh_token` | string | JWT refresh token previously minted by client session | #### Responses - **HTTP 200**: Token successfully rotated - Schema: `AuthTokenResponse` --- ### POST `/api/v1/auth/revoke` **Summary**: Revoke client session Revokes the active client session matching the provided access token or refresh token. - **Tags**: Authentication - **Operation ID**: `revokeAuthSession` - **Security**: Bearer JWT Token required #### Request Body Schema: `AuthRevokeRequest` | Field | Type | Description | | --- | --- | --- | | `refresh_token` | string | Optional raw refresh token to revoke if not revoking via Authorization header | #### Responses - **HTTP 200**: Session successfully revoked - **HTTP 400**: Missing token parameter - Schema: `ErrorResponse` - **HTTP 401**: Invalid token - Schema: `ErrorResponse` - **HTTP 404**: Session record not found - Schema: `ErrorResponse` --- ### DELETE `/api/v1/auth/revoke` **Summary**: Revoke client session Revokes the active client session matching the provided access token or refresh token. - **Tags**: Authentication - **Operation ID**: `deleteAuthSession` - **Security**: Bearer JWT Token required #### Request Body Schema: `AuthRevokeRequest` | Field | Type | Description | | --- | --- | --- | | `refresh_token` | string | Optional raw refresh token to revoke if not revoking via Authorization header | #### Responses - **HTTP 200**: Session successfully revoked - **HTTP 400**: Missing token parameter - Schema: `ErrorResponse` - **HTTP 401**: Invalid token - Schema: `ErrorResponse` - **HTTP 404**: Session record not found - Schema: `ErrorResponse` --- ### GET `/api/v1/me` **Summary**: Get current user profile & session Returns the authenticated user's details, timezone/theme preferences, permission flags (including `user_api`), active email accounts, and current client session metadata. - **Tags**: User Profile - **Operation ID**: `getCurrentUser` - **Security**: Bearer JWT Token required #### Responses - **HTTP 200**: User profile information - Schema: `MeResponse` - **HTTP 401**: Unauthorized - Schema: `ErrorResponse` - **HTTP 403**: Forbidden - User lacks user_api permission - Schema: `ErrorResponse` --- ### GET `/api/v1/emails` **Summary**: List emails Returns a paginated list of emails belonging to the user's active email accounts, ordered by received date descending. Supports filtering by bundle, seen status, snoozed state, category, and starred status. - **Tags**: Emails - **Operation ID**: `listEmails` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `bundle_id` | query | No | integer | Filter by bundle ID | | `seen` | query | No | boolean | Filter by read/seen status | | `include_snoozed` | query | No | string | Set to 'true' to include currently snoozed messages (unsnoozed by default) | | `category` | query | No | string | Filter by email category | | `starred` | query | No | boolean | Filter by starred status | | `page` | query | No | integer | Page number (1-indexed) | | `per_page` | query | No | integer | Results per page (1 to 100) | #### Responses - **HTTP 200**: Paginated list of emails - Schema: `EmailListResponse` - **HTTP 401**: Unauthorized - Schema: `ErrorResponse` --- ### GET `/api/v1/emails/{id}` **Summary**: Get email detail with transactional extractions Returns the complete email message details, RFC headers, body HTML/text, and extracted transactional structured data fields. - **Tags**: Emails - **Operation ID**: `getEmail` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `id` | path | Yes | integer | Email Message ID | #### Responses - **HTTP 200**: Email details - **HTTP 404**: Email not found - Schema: `ErrorResponse` --- ### GET `/api/v1/emails/{id}/thread` **Summary**: Get full conversation thread Retrieves all conversation messages in chronological order belonging to the specified email message's thread. - **Tags**: Emails - **Operation ID**: `getEmailThread` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `id` | path | Yes | integer | Email Message ID | #### Responses - **HTTP 200**: Thread details and conversation history - Schema: `ThreadResponse` - **HTTP 404**: Email or thread not found - Schema: `ErrorResponse` --- ### PATCH `/api/v1/emails/{id}/triage` **Summary**: Triage an email message Performs triage updates on an email: mark seen/unseen, star/unstar, snooze thread until a specific datetime with soft/hard wake rules, update bundle assignments, or re-categorize. - **Tags**: Emails - **Operation ID**: `triageEmail` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `id` | path | Yes | integer | Email Message ID | #### Request Body Schema: `TriageRequest` | Field | Type | Description | | --- | --- | --- | | `seen` | boolean | Update read/seen state | | `starred` | boolean | Star or unstar message | | `snoozed_until` | string | Snooze thread until datetime. Pass null to unsnooze. | | `snooze_type` | string | Soft snooze wakes on new incoming messages; hard snooze remains asleep until time. | | `bundle_ids` | array | List of bundle IDs to assign to this message | | `category` | string | Category name | | `subcategory` | string | Subcategory name | #### Responses - **HTTP 200**: Triage action successful - **HTTP 404**: Email not found - Schema: `ErrorResponse` --- ### POST `/api/v1/emails/send` **Summary**: Send or queue an email for delivery Queues an email message for delivery through Gmail API using the specified or default active email account. - **Tags**: Emails - **Operation ID**: `sendEmailMessage` - **Security**: Bearer JWT Token required #### Request Body Schema: `EmailSendRequest` | Field | Type | Description | | --- | --- | --- | | `email_account_id` | integer | Account ID to send from. Defaults to primary active account. | | `to_recipients` | array | Example: `["client@example.com"]` | | `cc_recipients` | array | | | `bcc_recipients` | array | | | `subject` | string | Example: `Meeting Follow-up` | | `body_text` | string | Example: `Thanks for speaking earlier today!` | | `body_html` | string | Optional HTML representation; auto-generated from body_text if omitted. | | `reply_to_message_id` | integer | Message ID this is in reply to. | #### Responses - **HTTP 200**: Email queued for delivery - **HTTP 400**: Missing required recipient or parameters - Schema: `ErrorResponse` --- ### GET `/api/v1/drafts` **Summary**: List active email drafts Returns all pending email drafts for the current user across active email accounts, ordered by last update descending. - **Tags**: Drafts - **Operation ID**: `listDrafts` - **Security**: Bearer JWT Token required #### Responses - **HTTP 200**: List of drafts --- ### POST `/api/v1/drafts` **Summary**: Create a new email draft Creates an email draft in 'draft' status. - **Tags**: Drafts - **Operation ID**: `createDraft` - **Security**: Bearer JWT Token required #### Request Body Schema: `DraftCreateRequest` | Field | Type | Description | | --- | --- | --- | | `email_account_id` | integer | | | `subject` | string | | | `body_text` | string | | | `body_html` | string | | | `to_recipients` | array | | | `cc_recipients` | array | | | `bcc_recipients` | array | | | `reply_to_message_id` | integer | | #### Responses - **HTTP 201**: Draft created --- ### GET `/api/v1/drafts/{id}` **Summary**: Get email draft details - **Tags**: Drafts - **Operation ID**: `getDraft` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `id` | path | Yes | integer | Draft ID | #### Responses - **HTTP 200**: Draft details - **HTTP 404**: Draft not found - Schema: `ErrorResponse` --- ### PATCH `/api/v1/drafts/{id}` **Summary**: Update an existing email draft - **Tags**: Drafts - **Operation ID**: `updateDraft` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `id` | path | Yes | integer | Draft ID | #### Request Body Schema: `DraftUpdateRequest` | Field | Type | Description | | --- | --- | --- | | `subject` | string | | | `body_text` | string | | | `body_html` | string | | | `to_recipients` | array | | | `cc_recipients` | array | | | `bcc_recipients` | array | | | `reply_to_message_id` | integer | | #### Responses - **HTTP 200**: Updated draft - **HTTP 404**: Draft not found - Schema: `ErrorResponse` --- ### DELETE `/api/v1/drafts/{id}` **Summary**: Delete an email draft - **Tags**: Drafts - **Operation ID**: `deleteDraft` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `id` | path | Yes | integer | Draft ID | #### Responses - **HTTP 200**: Draft successfully deleted - **HTTP 404**: Draft not found - Schema: `ErrorResponse` --- ### POST `/api/v1/drafts/{id}/send` **Summary**: Send a saved email draft Transitions the draft status to 'queued' and enqueues SendGmailMessageJob for delivery. - **Tags**: Drafts - **Operation ID**: `sendDraft` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `id` | path | Yes | integer | Draft ID | #### Responses - **HTTP 200**: Draft queued for sending - **HTTP 400**: Draft lacks valid recipients - Schema: `ErrorResponse` - **HTTP 404**: Draft not found - Schema: `ErrorResponse` --- ### GET `/api/v1/bundles` **Summary**: List user bundles Returns all organizational bundles defined for the user with calculated counts of unsnoozed, unseen messages. - **Tags**: Bundles - **Operation ID**: `listBundles` - **Security**: Bearer JWT Token required #### Responses - **HTTP 200**: List of bundles --- ### GET `/api/v1/search` **Summary**: Search emails and entities Executes full-text and entity search across the user's emails and bundles via SearchService. - **Tags**: Search - **Operation ID**: `searchEmails` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `q` | query | No | string | Search query term | | `limit` | query | No | integer | Maximum number of results to return (1 to 100) | #### Responses - **HTTP 200**: Search results - Schema: `SearchResponse` --- ### GET `/api/v1/search/suggestions` **Summary**: Get search autocomplete suggestions Returns fast prefix-based search suggestions for senders, bundles, and keywords. - **Tags**: Search - **Operation ID**: `getSearchSuggestions` - **Security**: Bearer JWT Token required #### Parameters | Name | In | Required | Type | Description | | --- | --- | --- | --- | --- | | `prefix` | query | Yes | string | Prefix characters entered by user | #### Responses - **HTTP 200**: List of suggestion strings ---