API beta docs

Build verified-domain agent email workflows.

These beta docs cover owner-email verification, API authentication, agent skill setup, custom-domain onboarding, agent mailboxes, sending, inbound webhooks, delivery events, rate limits, and delivery controls.

Start

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.
Instant start

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.
Endpoint

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.

Client libraries

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.

LanguageInstallFramework
Pythonpip install loftboxLangChain, CrewAI
TypeScript / JSnpm i @loftbox/sdkMCP
Gogo get github.com/TheMagicTower/loftbox-sdk-go
Rustcargo add loftbox-sdk
PHPcomposer require loftbox/sdkLaravel
Java / Kotlinnet.loftbox:loftbox-sdkSpring Boot
Rubygem install loftboxRails
C# / .NETdotnet add package LoftBox.SdkASP.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.

Authentication

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.

Agent setup

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.

Domains

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.

  1. Create the domain with POST /v1/domains.
  2. Fetch required records with GET /v1/domains/{id}/dns.
  3. Add the returned TXT, MX, DKIM, and return-path records at your DNS host.
  4. Call POST /v1/domains/{id}/verify after DNS propagation.
  5. 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 &rarr; domain onboard &rarr; 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 &amp; 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 &mdash; if an agent cannot find them, its installed bundle is out of date and re-running the installer adds them.

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 &mdash; 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 &mdash; 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.

Mailboxes

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_id for 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.

Outbound

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.

Mail clients

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.

PurposeHostPortSecurity
Sending (SMTP, recommended)mail.loftbox.net465SSL/TLS (implicit)
Sending (SMTP)mail.loftbox.net587STARTTLS
Receiving (IMAP)mail.loftbox.net993SSL/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

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.

Inbound

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.

Access control

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:manage or the key's parent.
DMARC

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 as next_cursor). Requires domain: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.

Events

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.
Controls

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
Delivery controls

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.