API Reference
Full REST API documentation.
API Reference
Base URL: http://localhost:3000 (dev) or https://api.lumbox.co (production)
Authentication
All endpoints (except /v1/orgs and /v1/inbound) require an API key:
X-API-Key: ak_your_key_hereThe inbound webhook uses a separate secret:
Authorization: Bearer {INBOUND_SECRET}Organizations
Create Organization
POST /v1/orgsBody:
{ "name": "my-org" }Response (201):
{
"id": "org_abc123",
"name": "my-org",
"api_key": "ak_live_...",
"message": "Save your API key - it won't be shown again",
"created_at": "2026-02-27T10:00:00Z"
}Get Current Organization
GET /v1/orgs/meResponse (200):
{
"id": "org_abc123",
"name": "my-org",
"inbox_count": 5,
"domain_count": 1,
"api_key_count": 2,
"created_at": "2026-02-27T10:00:00Z"
}Create an Account Claim Link
POST /v1/orgs/me/claimRequires a full organization API key with wildcard scope. Project keys, inbox-restricted keys, OAuth tokens, and internal service credentials are refused. Organizations must be active and have no owner. The link is single use and expires after 15 minutes. An account that already owns an organization cannot claim another one.
Response (201):
{
"org_id": "org_abc123",
"claim_url": "https://app.lumbox.co/claim/start?token=...",
"expires_at": "2026-10-02T10:15:00.000Z"
}Open the URL, sign in or sign up, and confirm the organization. Successful claims continue to billing. Do not share claim URLs publicly.
Create API Key
POST /v1/orgs/me/api-keysBody:
{ "name": "production", "scopes": ["inboxes:read", "inboxes:write"] }Response (201):
{
"id": "ak_id_abc",
"key_prefix": "ak_live_abc...",
"api_key": "ak_live_full_key_here",
"name": "production",
"scopes": ["inboxes:read", "inboxes:write"]
}Inboxes
Create Inbox
POST /v1/inboxesBody:
{
"name": "github-signup-bot",
"domain": "mycompany.com",
"local_part": "github-bot"
}All fields optional. Auto-generates address if local_part not set.
Response (201):
{
"id": "inb_abc123",
"address": "github-bot@mycompany.com",
"name": "github-signup-bot",
"status": "ACTIVE",
"created_at": "2026-02-27T10:00:00Z"
}List Inboxes
GET /v1/inboxesResponse (200):
{
"data": [
{
"id": "inb_abc123",
"address": "github-bot@lumbox.co",
"name": "github-signup-bot",
"status": "ACTIVE",
"email_count": 12,
"created_at": "2026-02-27T10:00:00Z"
}
]
}Get Inbox
GET /v1/inboxes/:idDelete Inbox
DELETE /v1/inboxes/:idPermanently deletes the inbox and all its emails.
List Emails in Inbox
GET /v1/inboxes/:id/emailsQuery params:
| Param | Type | Description |
|---|---|---|
limit | number | Max results (default: 20, max: 100) |
cursor | string | Pagination cursor |
from | string | Filter by sender (substring match) |
category | string | Filter by category |
since | string | ISO timestamp - only emails after this |
unread | boolean | Only unread emails |
Response (200):
{
"data": [/* email objects */],
"cursor": "next_cursor_value",
"has_more": true
}Get Email
GET /v1/inboxes/:id/emails/:emailIdReturns full email with attachments.
Wait for Email
GET /v1/inboxes/:id/waitLong-polls until a matching email arrives. Polls DB every 1 second.
Query params:
| Param | Type | Description |
|---|---|---|
timeout | number | Max seconds to wait (default: 30, max: 120) |
from | string | Filter by sender |
subject | string | Filter by subject keyword |
category | string | Filter by category |
has_otp | boolean | Only emails with OTP codes |
since | string | ISO timestamp |
Response (200): Full email object
Response (408): Timeout - no matching email arrived
Get OTP
GET /v1/inboxes/:id/otpLong-polls for the latest verification code. The simplest way to get an OTP.
Query params:
| Param | Type | Description |
|---|---|---|
timeout | number | Max seconds (default: 30, max: 120) |
from | string | Filter by sender |
since | string | ISO timestamp (default: 5 minutes before the request) |
Response (200):
{
"code": "847291",
"all_codes": ["847291"],
"from": "noreply@github.com",
"subject": "Your verification code",
"expires": "2026-02-27T10:05:00Z",
"email_id": "eml_abc123"
}Response (408): No OTP arrived before the timeout. For concurrent logins, see Parallel login runs.
Emails
Get Email (by ID)
GET /v1/emails/:idUpdate Email
PATCH /v1/emails/:idBody:
{ "is_read": true }Delete Email
DELETE /v1/emails/:idSearch Emails
GET /v1/emailsSearch across all inboxes in your organization.
Query params:
| Param | Type | Description |
|---|---|---|
q | string | Search subject, body, summary |
from | string | Filter by sender (substring) |
category | string | Filter by category |
inbox_id | string | Limit to specific inbox |
since | string | ISO timestamp |
limit | number | Max results (default: 20, max: 100) |
Send / Reply / Forward
Send Email
POST /v1/inboxes/:id/sendBody:
{
"to": ["recipient@example.com"],
"subject": "Hello from my agent",
"text": "Plain text body",
"html": "<h1>HTML body</h1>",
"cc": ["cc@example.com"]
}to and cc accept a string or array of strings.
Response (201):
{
"ok": true,
"email_id": "eml_abc123",
"message_id": "<msg_abc@lumbox.co>",
"provider_message_id": "ses-id",
"provider": "SES"
}Reply to Email
POST /v1/inboxes/:id/replyBody:
{
"email_id": "eml_abc123",
"text": "Thanks for the info!",
"reply_all": false
}Automatically handles In-Reply-To and References headers for threading.
Forward Email
POST /v1/inboxes/:id/forwardBody:
{
"email_id": "eml_abc123",
"to": "someone@example.com",
"comment": "FYI - see below"
}Threads
List Threads
GET /v1/threadsQuery params:
| Param | Type | Description |
|---|---|---|
inbox_id | string | Filter by inbox |
limit | number | Max results (default: 20) |
Response (200):
{
"data": [
{
"thread_id": "thr_abc123",
"subject": "Your order has shipped",
"last_from": "noreply@amazon.com",
"last_at": "2026-02-27T10:00:00Z",
"inbox_id": "inb_abc123",
"category": "transactional",
"message_count": 3
}
]
}Get Thread
GET /v1/threads/:threadIdReturns all emails in the thread plus assigned agents.
Response (200):
{
"thread_id": "thr_abc123",
"subject": "Your order has shipped",
"message_count": 3,
"agents": [
{ "id": "ta_abc", "agent_id": "support-bot", "role": "OWNER", "assigned_at": "..." }
],
"messages": [/* email objects */]
}Thread Agents
Assign Agent
POST /v1/threads/:id/assignBody:
{
"agent_id": "support-bot",
"role": "OWNER"
}Roles: OWNER, PARTICIPANT, OBSERVER (default: PARTICIPANT)
Upsert - re-assigning updates the role.
Unassign Agent
POST /v1/threads/:id/unassignBody:
{ "agent_id": "support-bot" }List Thread Agents
GET /v1/threads/:id/agentsDomains
Add Domain
POST /v1/domainsBody:
{ "domain": "mycompany.com" }Response (201):
{
"id": "dom_abc123",
"domain": "mycompany.com",
"status": "PENDING",
"dns_records": {
"verification": { "type": "TXT", "name": "mycompany.com", "value": "lumbox-verify=token" },
"mx": [
{ "type": "MX", "name": "mycompany.com", "value": "mx1.lumbox.co", "priority": 10 },
{ "type": "MX", "name": "mycompany.com", "value": "mx2.lumbox.co", "priority": 20 }
],
"spf": { "type": "TXT", "name": "mycompany.com", "value": "v=spf1 include:spf.lumbox.co ~all" },
"dkim": { "type": "CNAME", "name": "lumbox._domainkey.mycompany.com", "value": "dkim.lumbox.co" },
"dmarc": { "type": "TXT", "name": "_dmarc.mycompany.com", "value": "v=DMARC1; p=none; rua=mailto:dmarc@lumbox.co" }
}
}Verify Domain
POST /v1/domains/:id/verifyChecks DNS via Cloudflare DNS-over-HTTPS. Returns per-record status.
Response (200):
{
"id": "dom_abc123",
"domain": "mycompany.com",
"status": "ACTIVE",
"checks": {
"verification": true,
"mx": true,
"spf": true,
"dkim": true,
"dmarc": true
}
}List Domains
GET /v1/domainsDelete Domain
DELETE /v1/domains/:idSMTP Configs (BYOS)
Bring Your Own SMTP - configure custom outbound email providers.
Create Config
POST /v1/smtp-configsBody:
{
"provider": "SES",
"name": "production-ses",
"username": "AKIA...",
"password": "secret...",
"region": "us-east-1",
"from_email": "noreply@mycompany.com",
"is_default": true
}Providers: SMTP, SES, SENDGRID, POSTMARK, RESEND
List Configs
GET /v1/smtp-configsPasswords and API keys are masked in the response.
Delete Config
DELETE /v1/smtp-configs/:idTest Config
POST /v1/smtp-configs/:id/testBody:
{ "to": "test@example.com" }Set Default
PATCH /v1/smtp-configs/:id/defaultRouting Rules
Auto-assign agents to threads based on email properties.
Create Rule
POST /v1/routing-rulesBody:
{
"name": "Route billing emails",
"conditions": {
"category": "financial",
"from_pattern": "*@stripe.com",
"subject_pattern": "*invoice*"
},
"target_agent_id": "billing-agent",
"priority": 10
}At least one of target_agent_id or target_inbox_id required.
List Rules
GET /v1/routing-rulesUpdate Rule
PATCH /v1/routing-rules/:idDelete Rule
DELETE /v1/routing-rules/:idWebhooks
Create Webhook
POST /v1/webhooksBody:
{
"url": "https://myapp.com/webhook",
"events": ["email.received"]
}URL must use HTTPS. The secret is returned only once at creation.
Response (201):
{
"id": "wh_abc123",
"url": "https://myapp.com/webhook",
"events": ["email.received"],
"secret": "whsec_...",
"active": true
}Webhook Payload
{
"event": "email.received",
"timestamp": "2026-02-27T10:00:00Z",
"data": {
"email_id": "eml_abc123",
"inbox_id": "inb_abc123",
"from": "sender@example.com",
"subject": "Verification code",
"category": "verification",
"otp_codes": ["847291"],
"summary": "Contains a verification code"
}
}Signature header: X-Lumbox-Signature (HMAC-SHA256 hex)
List Webhooks
GET /v1/webhooksUpdate Webhook
PATCH /v1/webhooks/:idDelete Webhook
DELETE /v1/webhooks/:idTest Webhook
POST /v1/webhooks/:id/testList Deliveries
GET /v1/webhooks/:id/deliveriesCredentials (Vault)
Encrypted credential storage for AI agents. Values are never returned in API responses.
Store Credential
POST /v1/credentialsBody:
{
"service": "github.com",
"identifier": "bot@example.com",
"value": "MyS3cr3tP@ss!",
"credential_type": "password",
"label": "GitHub bot account",
"expires_at": "2027-01-01T00:00:00Z"
}Types: password, api_key, totp_secret, oauth_token, cookie, other
Response (201):
{
"id": "cred_abc123",
"service": "github.com",
"identifier": "bot@example.com",
"credential_type": "PASSWORD",
"stored": true,
"message": "Credential stored securely. Use 'use_credential_in_browser' to fill it into forms."
}List Credentials
GET /v1/credentials
GET /v1/credentials?service=github.comReturns service, identifier, type, label - never the values.
Delete Credential
DELETE /v1/credentials/:idUse Credential (Internal)
POST /v1/credentials/useDecrypts a credential for browser injection. Called by the MCP server's use_credential_in_browser tool. The plaintext is sent to the browser and never returned to the agent.
Audit Log
GET /v1/credentials/audit?limit=50Returns access logs for all credential operations.
Inbound Email (Internal)
Used by the mail server to deliver emails to the API.
POST /v1/inbound
Authorization: Bearer {INBOUND_SECRET}Body:
{
"from": "sender@example.com",
"to": ["inbox@lumbox.co"],
"subject": "Your verification code",
"text": "Your code is 847291",
"html": "<p>Your code is <b>847291</b></p>",
"headers": { "Message-ID": "<abc@example.com>" },
"attachments": [
{
"filename": "doc.pdf",
"content_type": "application/pdf",
"size": 12345,
"content": "base64..."
}
]
}Email Object
All email responses include:
{
"id": "eml_abc123",
"inbox_id": "inb_abc123",
"message_id": "<msg@domain>",
"thread_id": "thr_abc123",
"from_address": "sender@example.com",
"from_name": "Sender Name",
"to_addresses": [{ "name": null, "address": "inbox@lumbox.co" }],
"cc_addresses": [],
"subject": "Your verification code",
"text_body": "Your code is 847291",
"html_body": "<p>...</p>",
"stripped_text": "Your code is 847291",
"category": "verification",
"summary": "Verification email with OTP code",
"otp_codes": ["847291"],
"verification_links": [],
"magic_links": [],
"backup_codes": [],
"code_expiry": "2026-02-27T10:05:00Z",
"is_read": false,
"has_attachments": false,
"received_at": "2026-02-27T10:00:00Z"
}Error Responses
{ "error": "Description of the error" }| Status | Meaning |
|---|---|
| 400 | Bad request (missing/invalid fields) |
| 401 | Invalid API key or inbound secret |
| 403 | Forbidden (resource belongs to another org) |
| 404 | Resource not found |
| 408 | Timeout (wait/otp endpoints) |
| 409 | Conflict (duplicate address, etc.) |
| 500 | Internal server error |
| 503 | Inbox is paused |
Health Check
GET /health{ "status": "ok" }