Getting started
LoftBox personal beta starts with one owner email. Signup creates the organization and emails a 6-digit verification token. Only after owner-email verification does LoftBox return the first API key, and that plaintext key is shown once. Verification also sends the owner a welcome email with the beta limits and future admin/billing signup path.
Personal beta accounts are limited to 100 outbound sends per day. Mailbox message retention defaults to 7 days. Enterprise onboarding, billing, org membership, and larger limits are roadmap items.
- POST /v1/auth/signupCreate a personal beta organization and send the owner-email verification token.
- POST /v1/auth/signup/verifyVerify the owner email, receive the initial API key once, and trigger the owner welcome notice.
- POST /v1/agentsCreate an agent identity with owner, purpose, and policy scope.
- POST /v1/domainsStart custom-domain onboarding for a domain you control.
- POST /v1/agents/{agent_id}/mailboxesCreate a LoftBox-managed beta mailbox or a verified custom-domain mailbox.
- POST /v1/messagesSend operational email from the agent mailbox.
- GET /v1/mailboxes/{id}/inboxPoll unacknowledged inbound messages when the agent has no webhook endpoint.
Self-signup — instant API key
Get a working API key with a single unauthenticated call to POST /v1/auth/agent-signup. No console signup is required, so an agent can start instantly.
Until the owner verifies (claims) the account by email, the agent can send and receive only with that one owner address — a closed loop. The owner claims at loftbox.net/claim (the link is emailed automatically) or via the claim API, which lifts the restriction. Unclaimed accounts are deleted after 30 days.
curl -X POST https://api.loftbox.net/v1/auth/agent-signup \
-H 'content-type: application/json' \
-d '{"owner_email":"[email protected]"}'
# -> { "api_key": "lb_live_...", "mailbox_address": "[email protected]",
# "signup_status": "unverified" }
- POST /v1/auth/agent-signupInstantly create a restricted (unverified) account and return an API key once.
- POST /v1/auth/claim/startEmail a verification code to the owner address to begin claiming the account.
- POST /v1/auth/claim/verifyVerify the code to graduate the account to verified and lift the restricted mode.
Base URL and machine-readable docs
The canonical public API endpoint is https://api.loftbox.net. The homepage keeps a /api/* proxy path for preview verification, but production integrations should use the dedicated API hostname.
export BASE_URL="https://api.loftbox.net" export LOFTBOX_API_KEY="lb_live_replace_me"
Check the edge and schema before wiring an integration:
curl -i "$BASE_URL/health" curl -i "$BASE_URL/openapi.json" curl -i "$BASE_URL/llms.txt"
The https://loftbox.net/api route strips the /api prefix and forwards to the same Fly API origin. Use it for Pages preview checks, not as the primary integration URL.
Official SDKs
Official client libraries wrap the REST API in idiomatic, typed clients across eight languages — with popular framework integrations. Every SDK uses the same API key, the same approval/rate-limit/audit pipeline, and exposes the inbound safety signals: the prompt-injection score on inbound messages and the sender allow/block rules.
| Language | Install | Framework |
|---|---|---|
| Python | pip install loftbox | LangChain, CrewAI |
| TypeScript / JS | npm i @loftbox/sdk | MCP |
| Go | go get github.com/TheMagicTower/loftbox-sdk-go | — |
| Rust | cargo add loftbox-sdk | — |
| PHP | composer require loftbox/sdk | Laravel |
| Java / Kotlin | net.loftbox:loftbox-sdk | Spring Boot |
| Ruby | gem install loftbox | Rails |
| C# / .NET | dotnet add package LoftBox.Sdk | ASP.NET Core (DI) |
Sources and per-language quickstarts live in each GitHub repository under github.com/TheMagicTower (loftbox-sdk-<language>). Python, TypeScript, and Go are published to their registries; the remaining packages are rolling out to Packagist, Maven Central, RubyGems, NuGet, and crates.io — check the repo for current registry status.
API auth
Start by registering the owner email for the personal beta organization. The owner receives a verification token by email; the verification call returns the initial API key once and sends the owner welcome notice.
export LOFTBOX_OWNER_EMAIL="[email protected]" curl -i -X POST "$BASE_URL/v1/auth/signup" \ -H "Content-Type: application/json" \ -d '{ "email": "'"$LOFTBOX_OWNER_EMAIL"'", "organization_name": "Example Agents", "slug": "example-agents" }' export LOFTBOX_VERIFICATION_TOKEN="123456" curl -i -X POST "$BASE_URL/v1/auth/signup/verify" \ -H "Content-Type: application/json" \ -d '{ "email": "'"$LOFTBOX_OWNER_EMAIL"'", "verification_token": "'"$LOFTBOX_VERIFICATION_TOKEN"'" }'
Use the returned server-side API key with the Authorization header. Never expose API keys in browsers, mobile clients, public logs, or analytics events.
curl -i "$BASE_URL/v1/agents" \ -H "Authorization: Bearer $LOFTBOX_API_KEY"
Store keys in server-side secret storage such as Fly secrets, environment variables managed by the deploy platform, or a dedicated secret manager. Do not paste real keys into issue comments, screenshots, analytics tools, or browser code.
Rotate keys when an operator leaves, a secret may have leaked, or an environment no longer needs API access.
One-line install and agent prompt
Agents can install the public LoftBox mail skills, then run the onboarding flow with only the owner email. The registration skill derives the organization label, stable external_id, agent slug, and mailbox local part from the current agent. After registration, the installed send and inbox-check skills use the issued API key and mailbox ID.
curl -fsSL https://loftbox.net/install.sh | sh
curl -fsSL https://loftbox.net/install.sh | sh -s -- --agent codex curl -fsSL https://loftbox.net/install.sh | sh -s -- --agent claude curl -fsSL https://loftbox.net/install.sh | sh -s -- --target "$HOME/.my-agent/skills"
If the LoftBox mail skill is missing, install it with: curl -fsSL https://loftbox.net/install.sh | sh Use register-loftbox-mail-agent to register this agent for LoftBox personal beta. Ask me only for my owner email. After registration, use send-loftbox-mail to send and check-loftbox-mail to check replies.
curl -fsSL https://loftbox.net/install.sh | sh -s -- --check curl -fsSL https://loftbox.net/install.sh | sh -s -- --update
Use update checks as a notification path only. Installing a new skill bundle changes agent behavior, so updates should run after operator approval.
Only ask for another value if the agent identity cannot be derived or a duplicate external ID belongs to a different agent.
Custom-domain onboarding
Every outbound domain must be verified before mailboxes can use it. The API returns the DNS records needed for ownership, inbound routing, sender alignment, and delivery verification.
- Create the domain with
POST /v1/domains. - Fetch required records with
GET /v1/domains/{id}/dns. - Add the returned TXT, MX, DKIM, and return-path records at your DNS host.
- Call
POST /v1/domains/{id}/verifyafter DNS propagation. - Wait until inbound and outbound status are both verified before creating production mailboxes.
curl -i "$BASE_URL/v1/domains" \
-H "Authorization: Bearer $LOFTBOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"mail.example.com"}'
curl -i "$BASE_URL/v1/domains/domain_uuid/dns" \
-H "Authorization: Bearer $LOFTBOX_API_KEY"
curl -i -X POST "$BASE_URL/v1/domains/domain_uuid/verify" \
-H "Authorization: Bearer $LOFTBOX_API_KEY"
Or let an agent drive the whole business onboarding end to end with the unified onboard-business-domain flow (email auth → domain onboard → DNS). Paste this prompt into your agent:
If the LoftBox mail skill is missing, install it with: curl -fsSL https://loftbox.net/install.sh | sh Use onboard-business-domain to connect my business domain for verified sending. Ask me only for my owner email and the domain to send from. 1. Email auth: register my owner email. LoftBox emails a 6-digit token — ask me to paste it, then verify to obtain my organization API key (LoftBox-managed sending is available immediately once approved). 2. Domain onboard: run add <domain> (note the returned domain id), then read next_actions for the exact DNS records (TXT, DKIM, return-path, inbound MX). Show me each record (host, type, value). 3. DNS: let me add the records at my DNS host myself, or auto-apply them with apply-dns --provider route53|cloudflare using my own cloud credentials from the environment (they stay in my environment; LoftBox never receives them). 4. The domain re-verifies asynchronously on the server after DNS propagates. Poll status <domain_id> until inbound and outbound are both verified, then report — custom-domain sending activates once verified. Operate only on my organization. Do not fabricate DNS values; use exactly what the API returns. If onboarding cannot proceed (domain previously deleted, or in use by another org), report it and stop.
Official skills & source
onboard-business-domain and setup-loftbox-domain are official LoftBox skills. They are published in the public skill repository and installed by https://loftbox.net/install.sh — if an agent cannot find them, its installed bundle is out of date and re-running the installer adds them.
- Repository (public): github.com/TheMagicTower/loftbox-agent-mail-skill
- Skill directories:
onboard-business-domain/,setup-loftbox-domain/ - Installed bundle version & pinned commit:
loftbox.net/skill-version.json
The setup-loftbox-domain skill is invoked by running its script directly (the installer does not create a setup-loftbox-domain shell command). $SKILL_DIR is wherever install.sh placed the skill — e.g. ~/.claude/skills/setup-loftbox-domain, ~/.codex/skills/setup-loftbox-domain, or on Hermes ~/.hermes/skills/email/setup-loftbox-domain. The skill calls the LoftBox API — use exactly the DNS values the API returns, never fabricate them:
SKILL_DIR=<path to setup-loftbox-domain> # e.g. ~/.claude/skills/setup-loftbox-domain python3 "$SKILL_DIR/scripts/setup_loftbox_domain.py" add <domain> # onboard the domain (idempotent: resolves an existing active row on 409) python3 "$SKILL_DIR/scripts/setup_loftbox_domain.py" dns <id> # list the exact DNS records to set (TXT, DKIM, return-path, MX) python3 "$SKILL_DIR/scripts/setup_loftbox_domain.py" status <id> # structured status: inbound/outbound flags + next_actions python3 "$SKILL_DIR/scripts/setup_loftbox_domain.py" verify <id> # trigger verification after the DNS records are in place python3 "$SKILL_DIR/scripts/setup_loftbox_domain.py" apply-dns <id> --provider route53|cloudflare [--dry-run] [--overwrite] # auto-apply the next_actions records using cloud credentials from your OWN # environment (they never leave it). With no credentials, add the records # manually instead — onboarding still completes.
onboard-business-domain orchestrates register-loftbox-mail-agent (email auth) and setup-loftbox-domain (domain onboard + DNS) end to end. The 6-digit email token step requires a human, so it is not fully unattended.
For launch, personal beta uses LoftBox-managed delivery and LoftBox-managed inbound MX handling. Custom domains are enabled only after verification.
Agents and mailboxes
Agents hold the business purpose, ownership context, and stable external ID. Mailboxes attach an address and domain to that agent. Keep display names, owners, and policy scope clear enough for operator review.
- Use one mailbox per operational agent or workflow role.
- Use
external_idfor duplicate checks and idempotent agent setup. - Use verified custom domains for production outbound mail.
- Disable or hold autonomous sends when the agent purpose is unclear.
curl -i "$BASE_URL/v1/agents?external_id=hermes:support-agent" \ -H "Authorization: Bearer $LOFTBOX_API_KEY"
curl -i "$BASE_URL/v1/agents" \
-H "Authorization: Bearer $LOFTBOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Agent",
"slug": "support-agent",
"external_id": "hermes:support-agent",
"description": "Handles controlled support follow-up",
"purpose": "Reply to support conversations after policy checks",
"owner_label": "Customer Operations",
"policy_scope": "agent"
}'
curl -i "$BASE_URL/v1/agents/agent_uuid/mailboxes" \
-H "Authorization: Bearer $LOFTBOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"local_part": "support-agent",
"display_name": "Support Agent",
"retention_days": 7
}'
Omit domain_id for the LoftBox-managed personal beta domain. Register an agent webhook separately if the agent has an HTTPS endpoint.
On a verified custom domain you can mark one mailbox as catch-all ("is_catch_all": true at create or update). Any address at that domain with no exact mailbox then routes to it — useful for capturing support@, sales@, or anything@yourdomain with a single agent.
Sending messages
Send operational or transactional messages through the API from a verified mailbox. LoftBox records delivery references, delivery state, rate-limit decisions, and operator approval outcomes.
curl -i "$BASE_URL/v1/messages" \
-H "Authorization: Bearer $LOFTBOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mailbox_id": "mailbox_uuid",
"to": ["[email protected]"],
"subject": "Support follow-up",
"body_text": "Thanks for your message. We are checking this now.",
"metadata": { "ticket_id": "ticket_123" },
"send_at": "2030-01-01T09:00:00Z"
}'
Pass send_at (RFC3339, future) to schedule delivery, and an Idempotency-Key header to dedupe retries (a replay returns the original message). List and search sent or received messages with GET /v1/messages?q=<text>&label=<name>, and organize threads via POST/DELETE /v1/messages/{id}/labels.
LoftBox is not a bulk marketing sender. Purchased lists, scraped recipients, and generic SMTP relay use are outside the MVP policy.
IMAP / SMTP access
Besides the REST API and SDKs, you can connect any standard mail client or library over IMAP and SMTP. There is no separate mail password: authenticate with your LoftBox API key as the password and your full mailbox address as the username. Sending reuses the same pipeline as the API, so approval policy, rate limits, daily and storage caps, and audit logging all still apply.
| Purpose | Host | Port | Security |
|---|---|---|---|
| Sending (SMTP, recommended) | mail.loftbox.net | 465 | SSL/TLS (implicit) |
| Sending (SMTP) | mail.loftbox.net | 587 | STARTTLS |
| Receiving (IMAP) | mail.loftbox.net | 993 | SSL/TLS (implicit) |
Username is your mailbox address (for example bot-…@mail.loftbox.net), password is your API key — send scope for SMTP, receive scope for IMAP. IMAP exposes INBOX (incoming) and Sent (outgoing), read-only for now.
Mailboxes on the shared mail.loftbox.net domain support autoconfig: in Thunderbird and similar clients, entering the address and API key fills in the IMAP and SMTP settings automatically.
Inbound webhooks and polling
Replies are stored as inbound messages. Agents with an HTTPS endpoint can receive signed webhook events; agents without one should poll the mailbox inbox and acknowledge each processed message.
- message.inboundA new inbound message or reply was parsed, threaded, and made available to the configured webhook.
- message.deliveredDelivery was reported for the recipient.
- message.failedOutbound delivery failed permanently or exhausted retry handling.
curl -i "$BASE_URL/v1/agents/agent_uuid/webhooks" \
-H "Authorization: Bearer $LOFTBOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/loftbox",
"event_types": [
"message.inbound",
"message.delivered"
]
}'
The webhook secret is returned once when the webhook is created. Store it immediately and verify every inbound signature before trusting event content.
Every message.inbound event carries an injection_risk object — a prompt-injection heuristic score (0–1) plus the matched categories (instruction override, role hijack, tool injection, data exfiltration, hidden text, encoding evasion). The same value is on the message as injection_score. It is a signal only: LoftBox never blocks on it, so your agent decides how to act on untrusted inbound content — for example, requiring human approval above a threshold before following instructions found in an email.
To control which senders can reach an agent at all, manage per-mailbox or org-wide inbound allow/block lists via /v1/inbound-rules. Block a sender address or domain, or set an allowlist so only approved senders are accepted — anything else is refused at receive time (SMTP 550). Rules are evaluated against the SMTP envelope sender, not the spoofable From header.
curl -i "$BASE_URL/v1/mailboxes/mailbox_uuid/inbox?limit=20" \
-H "Authorization: Bearer $LOFTBOX_API_KEY"
curl -i -X POST "$BASE_URL/v1/mailboxes/mailbox_uuid/inbox/ack" \
-H "Authorization: Bearer $LOFTBOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"message_ids":["message_uuid_or_msg_public_id"]}'
Only acknowledge after the agent has durably processed the message. Empty inbox polling should back off; active polling should use a moderate interval such as 30-60 seconds.
Realtime streaming (WebSocket)
Agents that cannot host an HTTPS webhook (for example, running behind NAT or a firewall) can subscribe to events in realtime over a WebSocket. The stream carries the same event types as webhooks, with sub-second delivery and cursor-based replay of events missed while disconnected. Requires the receive scope.
- GET /v1/wsWebSocket upgrade. Authenticate with
Authorization: Bearer. Filter with?event_types=and?agent_id=; resume with?after=<cursor>. - GET /v1/ws/asyncapi.jsonAsyncAPI specification for the stream (public).
wscat -c "wss://api.loftbox.net/v1/ws?event_types=message.inbound" \
-H "Authorization: Bearer $LOFTBOX_API_KEY"
# frames: {"type":"ready",...} then {"type":"event","event":{...},"cursor":"..."}
# on reconnect, pass ?after=<last cursor> to replay missed events
On an error frame with code lagged or resync_required, reconnect using the last received cursor.
Permissions and API keys
API keys carry scopes. Coarse scopes (send, receive, admin) imply fine-grained scopes (message:send, message:read, domain:manage, approval:decide, keys:manage, …), so existing keys keep working while you can issue least-privilege child keys for sub-agents.
- POST /v1/auth/keysIssue a child key. Requested scopes must be a subset of the caller's (no privilege escalation). Optional
expires_in_days. Plaintext key returned once. - GET /v1/auth/keysList org keys (metadata only, no secrets). Requires
keys:manage. - DELETE /v1/auth/keys/{id}Revoke a key and all of its descendants (cascade). Allowed for
keys:manageor the key's parent.
DMARC aggregate reports
Mailbox providers (Gmail, Outlook, Yahoo, …) send daily DMARC aggregate (rua) reports summarizing which source IPs sent mail as your verified domains and whether SPF/DKIM/DMARC aligned. LoftBox ingests these into structured reports so you can monitor deliverability and spot spoofing. Each stored report also records the authenticated reporter domain.
- GET /v1/dmarc/reportsList your organization's DMARC aggregate reports, newest first. Cursor pagination via
before_date_end+before_id(returned together asnext_cursor). Requiresdomain:read. - GET /v1/dmarc/reports/{id}Retrieve one report with its per-source-IP records (message count, disposition, DKIM/SPF alignment, header-from). Requires
domain:read.
curl -i "$BASE_URL/v1/dmarc/reports" \ -H "Authorization: Bearer $LOFTBOX_API_KEY" curl -i "$BASE_URL/v1/dmarc/reports/REPORT_ID" \ -H "Authorization: Bearer $LOFTBOX_API_KEY"
Reports appear as providers submit them (typically once per day per reporter). Only reports for your own verified domains are returned; platform or unhosted-domain reports are never exposed.
Delivery events
Delivery status is normalized across managed delivery callbacks and internal send state. Use event IDs and delivery references for support, not private recipient data.
queued: accepted by LoftBox for delivery.sent: accepted for outbound delivery.delivered: delivery was reported for the recipient.failed: delivery failed permanently or exhausted retries.blocked: policy, rate limit, or report control blocked sending.
Rate limits and report controls
Outbound sending is limited by organization, agent, mailbox, recipient, and domain policy. Personal beta organizations are capped at 100 sends per day. High-risk messages can be held for approval before delivery is attempted.
Handle 429 responses by backing off. Do not retry indefinitely or bypass approval holds from another mailbox.
HTTP/1.1 429 Too Many Requests Retry-After: 60
Managed delivery and safety setup
LoftBox operates managed outbound delivery for beta accounts. Public documentation should describe the customer-visible controls without exposing internal vendor names, credentials, or routing details.
- Business nature: API-managed operational email identities for verified-domain AI agents.
- Email types: transactional/system notifications, agent replies initiated by verified users, onboarding/test messages, and report/contact handling.
- Controls: verified domains only, explicit rate limits, delivery logs, suppression/complaint handling, and monitored report contact.
- Public pages: Privacy, Terms, and Anti-spam policy.
DNS propagation and delivery verification holds should be treated as setup state, not as proof that the API proxy or homepage docs are broken.