← Back to API Documentation

Inbox API Documentation

Email receiving and processing API for automation workflows.

Quick Reference
Base URL:
https://api.ainoflow.io
Authentication:
Authorization: Bearer your_api_key_here

Core Concepts

REST path and list responses
  • • All routes below use the prefix /api/v1/inbox
  • • List endpoints accept aggregate (default false): when false, the body is only the items array and pagination is in X-Total-Count, X-Page, X-Limit, X-Total-Pages
  • • Responses use camelCase JSON property names
Hook Address
  • • Globally unique email address per inbox
  • • Generated server-side with embedded HMAC signature
  • • Example: habcd1234-x7z8wq3m5n2kabcd@ainobox.com
  • • API operations use inbox GUID, not hook address
Multi-Tenant Isolation
  • • Data scoped to tenant (scopeId) and project (projectId)
  • • Inboxes and messages only visible within the authenticated scope/project
  • • Storage paths under inbox/{scopeId}/{projectId}/...
Sender Whitelisting
  • • Optional allowedFromEmails field
  • • Comma or semicolon delimited, normalized on save
  • • Case-insensitive, exact match only
  • null = accept all senders
Message Lifecycle
  • Received - email arrives, parsed, stored
  • Handled - consumer calls /handle
  • Expired - TTL reached, auto-deleted with files
  • Deleted - manual removal via DELETE
TTL System
  • ttlDays: Default 30 days, configurable per inbox
  • Automatic cleanup: Messages + attachments deleted on expiry
  • Deduplication: SHA-256 hash of raw email content
  • isHandled: Processing flag, not retention flag
IMAP Email Polling
  • Multiple sources: Attach multiple IMAP configs per inbox
  • Auto-polling: Configurable interval (1-1440 min)
  • After-fetch actions: Leave untouched, mark read, or delete on server
  • Same pipeline: Deduplication, whitelisting, webhooks all apply

Main Endpoints

POST
Create Inbox
/api/v1/inbox
Create a new virtual email inbox. Returns the full inbox object with generated hook address.

Request Body:

id
optional
- Inbox GUID (auto-generated if omitted)
name
required
- Human-readable label (max 100 chars)
allowedFromEmails
optional
- Sender whitelist, comma/semicolon delimited. null = accept all
webhookUrl
optional
- Push URL for new messages; must be a public host (SSRF-safe). See docs for restrictions
ttlDays
optional
- Message retention in days (default: 30, must be positive)
maxMessageSizeBytes
optional
- Max email size in bytes (default: 10485760 = 10MB)
maxAttachments
optional
- Max attachments per email (default: 10)
enabled
optional
- Whether the inbox is enabled (default: true). Disabled inboxes reject incoming emails

Response (201 Created):

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "scopeId": "1fa85f64-5717-4562-b3fc-2c963f66afa6",
  "projectId": "2fa85f64-5717-4562-b3fc-2c963f66afa6",
  "hookAddress": "habcd1234-x7z8wq3m5n2kabcd@ainobox.com",
  "name": "Invoice intake",
  "allowedFromEmails": "partner@corp.com, alerts@service.com",
  "webhookUrl": "https://myapp.com/webhook/inbox",
  "ttlDays": 30,
  "maxMessageSizeBytes": 10485760,
  "maxAttachments": 10,
  "enabled": true,
  "createdAt": "2026-02-20T10:30:00Z",
  "createdBy": "api-key-name"
}
GET
List Inboxes
/api/v1/inbox
All inboxes for the authenticated scope/project. Supports pagination and aggregate.

Query Parameters:

page
optional
- Page number 1-based (default: 1); cannot combine with offset
limit
optional
- Items per page (default: 50, max: 5000)
offset
optional
- Skip N items instead of page
sortBy
optional
- Name or CreatedAt (default: CreatedAt)
sortOrder
optional
- Asc or Desc (default: Desc)
aggregate
optional
- If true, body includes items + totalCount, page, pageSize, totalPages

Response (200 OK):

List items include messageCount, unhandledCount, imapSourceCount.

GET
Get Inbox
/api/v1/inbox/{id}
Single inbox with full configuration and computed counts.

Path Parameters:

id
required
- Inbox ID (GUID)
PUT
Update Inbox
/api/v1/inbox/{id}
Update inbox configuration. Only provided fields are updated.

Request Body:

name
optional
- Updated label
allowedFromEmails
optional
- Updated sender whitelist. null to remove filter
webhookUrl
optional
- Updated webhook URL. null to remove. Must be a public host when set
ttlDays
optional
- Updated retention period (must be positive)
maxMessageSizeBytes
optional
- Updated max email size per message
maxAttachments
optional
- Updated max attachments per message
enabled
optional
- Enable or disable the inbox

Response (200 OK):

Partial summary: id, hookAddress, updatedAt.

DELETE
Delete Inbox
/api/v1/inbox/{id}
Delete an inbox and all its messages and attachments. Cascading delete - all associated data permanently removed. Use ?force=true to delete even if unhandled messages exist.

Query Parameters:

force
optional
- When true, deletes even if unhandled messages exist. Default: false (returns 400 if unhandled messages)

Response (200 OK):

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "hookAddress": "habcd1234-x7z8wq3m5n2kabcd@ainobox.com"
}
GET
List Messages
/api/v1/inbox/{id}/messages
Get paginated list of messages for an inbox. Primary endpoint for polling-based consumption. Returns full message content including body text and attachment download URLs (same pattern as Storage list including values). bodyHtml is not included in list items; use Get Message or Handle for HTML body.

Query Parameters:

page
optional
- Page number (1-based, default: 1); cannot combine with offset
limit
optional
- Items per page (default: 50, max: 5000)
offset
optional
- Skip N items instead of page
isHandled
optional
- Filter: true = handled only, false = unhandled only, omitted = all
sortBy
optional
- ReceivedAt (default), FromAddress, Subject, SizeBytes
sortOrder
optional
- Asc or Desc (default: Desc)
receivedAfter
optional
- Only messages received after (ISO 8601)
receivedBefore
optional
- Only messages received before (ISO 8601)
fromAddress
optional
- Filter by sender (case-insensitive contains)
aggregate
optional
- If true, body includes items + totalCount, page, pageSize, totalPages

Response (200 OK):

{
  "items": [{
    "id": "7fa85f64-...",
    "fromAddress": "sender@example.com",
    "subject": "Invoice #12345",
    "bodyText": "Please find attached...",
    "isHandled": false,
    "hasAttachments": true,
    "attachments": [{
      "id": "9fa85f64-...",
      "fileName": "invoice.pdf",
      "downloadUrl": "https://..."
    }]
  }],
  "totalCount": 42
}
GET
Get Message
/api/v1/inbox/{id}/messages/{messageId}
Full message including bodyHtml and attachment download URLs. Read-only — does not mark the message as handled.

Path Parameters:

id
required
- Inbox ID (GUID)
messageId
required
- Message ID (GUID)
POST
Handle Message
/api/v1/inbox/{id}/messages/{messageId}/handle
Returns full message content AND marks as handled in one atomic operation. Idempotent - repeated calls return success.

Path Parameters:

id
required
- Inbox ID (GUID)
messageId
required
- Message ID (GUID)

Response (200 OK):

Full message with body, attachments, and download URLs. isHandled: true.

POST
Batch Handle Messages
/api/v1/inbox/{id}/messages/handle
Handle multiple messages in one call. Provide specific IDs or handle first N unhandled messages (default: 10, max: 100).

Request Body:

messageIds
optional
- Specific message IDs to handle. If null/empty, handles first N unhandled
limit
optional
- Max messages to handle when messageIds not provided (default: 10, max: 100)

Response (200 OK):

{ items, handledCount } — full message objects in items.

DELETE
Delete Message
/api/v1/inbox/{id}/messages/{messageId}
Delete a single message and all its attachments from both database and storage.

Response (200 OK):

{
  "id": "7fa85f64-5717-4562-b3fc-2c963f66afa6"
}
GET
Download Attachment (Redirect)
/api/v1/inbox/{id}/messages/{messageId}/attachments/{attachmentId}
Returns 302 Found with Location set to a pre-signed S3 URL. Use curl -L or a browser; use the /url endpoint if redirects are not followed.

Path Parameters:

id
required
- Inbox ID (GUID)
messageId
required
- Message ID (GUID)
attachmentId
required
- Attachment ID (GUID)
GET
Get Attachment Download URL
/api/v1/inbox/{id}/messages/{messageId}/attachments/{attachmentId}/url
Get a pre-signed URL for direct attachment download. Use when your HTTP client doesn't follow redirects.

Query Parameters:

expirySeconds
optional
- URL expiration time in seconds (default: 3600, min: 60, max: 86400)

Response (200 OK):

{
  "downloadUrl": "https://storage.example.com/inbox/...?signature=...",
  "downloadUrlExpiresAt": "2026-02-20T11:30:00Z"
}

Webhook Notifications

When webhookUrl is set, the service POSTs after a new message is stored. Delivery is non-blocking (retries with backoff). Webhook URLs must use http/https and resolve to a public host (private/loopback/metadata IPs are blocked at create/update and at delivery). Use the message id from the payload to fetch full content. Payload is camelCase; outer shape matches the Convert API webhook style.

Webhook body (excerpt)
result holds summary fields; fetch full message via GET or POST handle.
{
  "result": {
    "id": "7fa85f64-5717-4562-b3fc-2c963f66afa6",
    "inboxId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "inboxHookAddress": "habcd1234-x7z8wq3m5n2kabcd@ainobox.com",
    "fromAddress": "sender@example.com",
    "subject": "Invoice #12345",
    "sizeBytes": 245760,
    "receivedAt": "2026-02-20T10:30:00Z",
    "isHandled": false,
    "hasAttachments": true,
    "attachmentCount": 2
  }
}

IMAP Sources

IMAP sources allow an inbox to automatically pull emails from external mailboxes. Each inbox can have multiple IMAP sources. Fetched emails go through the same pipeline as Cloudflare-delivered emails — same deduplication, attachments, TTL, and webhook notifications.

POST
Add IMAP Source
/api/v1/inbox/{id}/imap
Add an IMAP source to an inbox. Passwords are encrypted at rest (AES-256-GCM). Duplicate protection: one config per (host, username, folder) per inbox — duplicates return 409 Conflict. 404 if inbox missing.

Request Body:

id
optional
- IMAP source GUID (auto-generated if omitted)
host
required
- IMAP server hostname (e.g. imap.gmail.com)
username
required
- IMAP account username / email address
password
required
- IMAP account password or app password
port
optional
- IMAP port (default: 993)
useSsl
optional
- Use SSL/TLS (default: true)
folder
optional
- IMAP folder to watch (default: INBOX)
fetchIntervalMinutes
optional
- Polling interval in minutes (min: 1, max: 1440, default: 5)
afterFetchAction
optional
- Action after ingestion: none (default), read, delete

Response (201 Created):

{
  "id": "4fa85f64-5717-4562-b3fc-2c963f66afa6",
  "inboxId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "host": "imap.gmail.com",
  "port": 993,
  "useSsl": true,
  "username": "invoices@mycompany.com",
  "folder": "INBOX",
  "fetchIntervalMinutes": 5,
  "afterFetchAction": "none"
}
GET
List IMAP Sources
/api/v1/inbox/{id}/imap
List all IMAP sources configured for an inbox. Passwords are never returned.
PUT
Update IMAP Source
/api/v1/inbox/{id}/imap/{imapId}
Update an IMAP source configuration. Only provided fields are updated. Omit password to keep existing.
DELETE
Delete IMAP Source
/api/v1/inbox/{id}/imap/{imapId}
Remove an IMAP source. The inbox continues receiving via other sources and Cloudflare push.
POST
Trigger IMAP Fetch
/api/v1/inbox/{id}/imap/fetch
Run fetch for all IMAP sources on the inbox. Returns 403 Forbidden if the inbox is disabled. Use the variant below for one source only.

Response (200 OK):

{
  "results": [{
    "imapConfigId": "4fa85f64-...",
    "success": true,
    "fetchedCount": 3,
    "ingestedCount": 2,
    "errorMessage": null
  }]
}
POST
Trigger IMAP Fetch (one source)
/api/v1/inbox/{id}/imap/{imapId}/fetch
Same response shape as fetch-all; results contains one entry. 403 if inbox disabled; 404 if inbox or IMAP config missing.

Example Usage

cURL Examples

Create inbox

curl -X POST "https://api.ainoflow.io/api/v1/inbox" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Invoice intake", "ttlDays": 30}'

List inboxes

curl "https://api.ainoflow.io/api/v1/inbox?page=1&limit=50&sortBy=CreatedAt&sortOrder=Desc" \
  -H "Authorization: Bearer YOUR_API_KEY"

Poll for unhandled messages

curl "https://api.ainoflow.io/api/v1/inbox/{id}/messages?isHandled=false&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Get message (read-only)

curl "https://api.ainoflow.io/api/v1/inbox/{id}/messages/{messageId}" \
  -H "Authorization: Bearer YOUR_API_KEY"

Handle a message

curl -X POST "https://api.ainoflow.io/api/v1/inbox/{id}/messages/{messageId}/handle" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json"

Batch handle unhandled messages

curl -X POST "https://api.ainoflow.io/api/v1/inbox/{id}/messages/handle" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit": 10}'

Delete inbox

curl -X DELETE "https://api.ainoflow.io/api/v1/inbox/{id}" \
  -H "Authorization: Bearer YOUR_API_KEY"

n8n / Make.com Integration

Polling Workflow Pattern
Process incoming emails with a simple polling pattern
1. Schedule Trigger (every 5 min):
   → Start polling cycle

2. HTTP Request Node:
   GET /api/v1/inbox/{id}/messages?isHandled=false&limit=10
   → Returns unhandled messages with full content

3. Loop / Split In Batches:
   → Process each message

4. For each message:
   → Extract body text, attachments
   → Download attachments via downloadUrl
   → Run business logic (OCR, routing, etc.)

5. HTTP Request Node (after processing):
   POST /api/v1/inbox/{id}/messages/{messageId}/handle
   → Mark as handled

HTTP Status Codes

200
OK - Request successful (list, get, update, delete, handle)
201
Created - Inbox created successfully
302
Found - Attachment download redirect to pre-signed URL
400
Bad Request - Invalid parameters, missing required fields, blocked webhook URL
401
Unauthorized - Missing or invalid API key
403
Forbidden - e.g. IMAP fetch when inbox is disabled
404
Not Found - Inbox, message, or attachment not found in current scope/project
409
Conflict - Duplicate IMAP source (same host, username, folder) for the inbox
429
Too Many Requests - Max inboxes per scope reached
500
Internal Server Error - Server error

Best Practices

Polling Strategy

  • • Poll every 1-5 minutes for most use cases
  • • Use isHandled=false filter
  • • Handle messages after processing

Webhook Usage

  • • Use webhooks for real-time processing
  • • Fetch full content via API after webhook
  • • Webhook delivery is non-blocking

Attachment Handling

  • • Download URLs included in list/handle responses
  • • URLs expire in 1 hour by default
  • • Use /url endpoint for fresh URLs

Sender Whitelisting

  • • Restrict senders for production inboxes
  • • Non-whitelisted emails silently rejected
  • • Set to null to accept all senders

Ready to Get Started?

Create your free account and start receiving emails in your workflows in minutes.