Lumbox Docs

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_here

The inbound webhook uses a separate secret:

Authorization: Bearer {INBOUND_SECRET}

Organizations

Create Organization

POST /v1/orgs

Body:

{ "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/me

Response (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 API Key

POST /v1/orgs/me/api-keys

Body:

{ "name": "production", "scopes": ["inbox:read", "inbox:write"] }

Response (201):

{
  "id": "ak_id_abc",
  "key_prefix": "ak_live_abc...",
  "api_key": "ak_live_full_key_here",
  "name": "production",
  "scopes": ["inbox:read", "inbox:write"]
}

Inboxes

Create Inbox

POST /v1/inboxes

Body:

{
  "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/inboxes

Response (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/:id

Delete Inbox

DELETE /v1/inboxes/:id

Permanently deletes the inbox and all its emails.

List Emails in Inbox

GET /v1/inboxes/:id/emails

Query params:

ParamTypeDescription
limitnumberMax results (default: 20, max: 100)
cursorstringPagination cursor
fromstringFilter by sender (substring match)
categorystringFilter by category
sincestringISO timestamp — only emails after this
unreadbooleanOnly unread emails

Response (200):

{
  "data": [/* email objects */],
  "cursor": "next_cursor_value",
  "has_more": true
}

Get Email

GET /v1/inboxes/:id/emails/:emailId

Returns full email with attachments.

Wait for Email

GET /v1/inboxes/:id/wait

Long-polls until a matching email arrives. Polls DB every 1 second.

Query params:

ParamTypeDescription
timeoutnumberMax seconds to wait (default: 30, max: 120)
fromstringFilter by sender
subjectstringFilter by subject keyword
categorystringFilter by category
has_otpbooleanOnly emails with OTP codes
sincestringISO timestamp

Response (200): Full email object

Response (408): Timeout — no matching email arrived

Get OTP

GET /v1/inboxes/:id/otp

Long-polls for the latest verification code. The simplest way to get an OTP.

Query params:

ParamTypeDescription
timeoutnumberMax seconds (default: 30, max: 120)
fromstringFilter by sender
sincestringISO timestamp

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"
}

Emails

Get Email (by ID)

GET /v1/emails/:id

Update Email

PATCH /v1/emails/:id

Body:

{ "is_read": true }

Delete Email

DELETE /v1/emails/:id

Search Emails

GET /v1/emails

Search across all inboxes in your organization.

Query params:

ParamTypeDescription
qstringSearch subject, body, summary
fromstringFilter by sender (substring)
categorystringFilter by category
inbox_idstringLimit to specific inbox
sincestringISO timestamp
limitnumberMax results (default: 20, max: 100)

Send / Reply / Forward

Send Email

POST /v1/inboxes/:id/send

Body:

{
  "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/reply

Body:

{
  "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/forward

Body:

{
  "email_id": "eml_abc123",
  "to": "someone@example.com",
  "comment": "FYI — see below"
}

Threads

List Threads

GET /v1/threads

Query params:

ParamTypeDescription
inbox_idstringFilter by inbox
limitnumberMax 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/:threadId

Returns 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/assign

Body:

{
  "agent_id": "support-bot",
  "role": "OWNER"
}

Roles: OWNER, PARTICIPANT, OBSERVER (default: PARTICIPANT)

Upsert — re-assigning updates the role.

Unassign Agent

POST /v1/threads/:id/unassign

Body:

{ "agent_id": "support-bot" }

List Thread Agents

GET /v1/threads/:id/agents

Domains

Add Domain

POST /v1/domains

Body:

{ "domain": "mycompany.com" }

Response (201):

{
  "id": "dom_abc123",
  "domain": "mycompany.com",
  "status": "PENDING",
  "dns_records": {
    "verification": { "type": "TXT", "name": "_lumbox.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/verify

Checks 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/domains

Delete Domain

DELETE /v1/domains/:id

SMTP Configs (BYOS)

Bring Your Own SMTP — configure custom outbound email providers.

Create Config

POST /v1/smtp-configs

Body:

{
  "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-configs

Passwords and API keys are masked in the response.

Delete Config

DELETE /v1/smtp-configs/:id

Test Config

POST /v1/smtp-configs/:id/test

Body:

{ "to": "test@example.com" }

Set Default

PATCH /v1/smtp-configs/:id/default

Routing Rules

Auto-assign agents to threads based on email properties.

Create Rule

POST /v1/routing-rules

Body:

{
  "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-rules

Update Rule

PATCH /v1/routing-rules/:id

Delete Rule

DELETE /v1/routing-rules/:id

Webhooks

Create Webhook

POST /v1/webhooks

Body:

{
  "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/webhooks

Update Webhook

PATCH /v1/webhooks/:id

Delete Webhook

DELETE /v1/webhooks/:id

Test Webhook

POST /v1/webhooks/:id/test

List Deliveries

GET /v1/webhooks/:id/deliveries

Credentials (Vault)

Encrypted credential storage for AI agents. Values are never returned in API responses.

Store Credential

POST /v1/credentials

Body:

{
  "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.com

Returns service, identifier, type, label — never the values.

Delete Credential

DELETE /v1/credentials/:id

Use Credential (Internal)

POST /v1/credentials/use

Decrypts 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=50

Returns 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" }
StatusMeaning
400Bad request (missing/invalid fields)
401Invalid API key or inbound secret
403Forbidden (resource belongs to another org)
404Resource not found
408Timeout (wait/otp endpoints)
409Conflict (duplicate address, etc.)
500Internal server error
503Inbox is paused

Health Check

GET /health
{ "status": "ok" }