Skip to content

Add subdomain inboxes — receive email on custom subdomains#10

Merged
sragss merged 5 commits intomainfrom
feat/subdomain-inboxes
Feb 13, 2026
Merged

Add subdomain inboxes — receive email on custom subdomains#10
sragss merged 5 commits intomainfrom
feat/subdomain-inboxes

Conversation

@sragss
Copy link
Copy Markdown
Contributor

@sragss sragss commented Feb 13, 2026

User Goal

"Wait I thought we just made that... Why don't we have that feature?"

An agent tried to buy an inbox on a subdomain (e.g., biden@craig.x402email.com) and discovered we only supported buying inboxes on the root domain (username@x402email.com). Subdomain owners could send but not receive. This PR adds full inbound email support for subdomains.

Summary

  • Per-address inboxes on subdomains — subdomain owners create inboxes like biden@craig.x402email.com ($0.25 x402, cap 100/subdomain)
  • 500 message cap per inbox — forward handler skips retention at cap; messages API warns at 80% and 100% with actionable delete instructions
  • Optional forwarding + message retention — each inbox can forward to a real address, retain messages for API access, or both
  • Catch-all forwarding — subdomain-level fallback for unmatched addresses via POST /api/subdomain/update
  • Messages API — same pattern as root inbox messages ($0.001 x402 for list/read, free SIWX for delete)

Pricing rationale

$0.25/inbox covers ~1,250 forwarded emails at our SES cost (~3+ years of light use per inbox). 500 message cap prevents unbounded S3 growth. Agents get clear warnings via messageCount/messageLimit/warning fields when they interact with their inbox.

New Endpoints

Endpoint Cost Auth
POST /api/subdomain/inbox/create $0.25 x402 (owner)
POST /api/subdomain/inbox/list free SIWX (owner)
POST /api/subdomain/inbox/delete free SIWX (owner)
POST /api/subdomain/inbox/messages $0.001 x402 (owner)
POST /api/subdomain/inbox/messages/read $0.001 x402 (owner)
POST /api/subdomain/inbox/messages/delete free SIWX (owner)
POST /api/subdomain/update free SIWX (owner)

Changes

  • Schema: SubdomainInbox + SubdomainMessage models, catchAllForwardTo on Subdomain
  • Forward handler: branches on recipient domain — subdomain recipients routed to matching inbox or catch-all, 500 message cap enforced
  • DNS: MX record added to subdomain provisioning (10 inbound-smtp.us-east-1.amazonaws.com)
  • Pricing: subdomainInboxCreate ($0.25), subdomainInboxMessages ($0.001)
  • Capacity warnings: messages list + read return messageCount, messageLimit, and warning when >=80% full
  • Discovery + docs: .well-known/x402 instructions + llms.txt updated with all new endpoints

Manual Step (post-deploy)

Add *.x402email.com as recipient condition to the existing SES receipt rule in AWS console. This enables SES to accept inbound email for all subdomains.

Test plan

  • Deploy + update SES receipt rule
  • Create inbox: {subdomain: "craig", localPart: "biden"} → success, costs $0.25
  • Send email to biden@craig.x402email.com → message retained in DB
  • List messages → returns messageCount and messageLimit fields
  • Read/delete messages via API
  • Set catch-all: {subdomain: "craig", catchAllForwardTo: "catch@example.com"} → email to unknown@craig forwards
  • No catch-all, no inbox → email silently dropped
  • Verify pnpm build passes

🤖 Generated with Claude Code

Subdomain owners can now create per-address inboxes (e.g., biden@craig.x402email.com)
with optional forwarding and message retention for programmatic API access.

Schema: SubdomainInbox + SubdomainMessage models, catchAllForwardTo on Subdomain
Forward handler: routes subdomain recipients to matching inbox or catch-all
DNS: MX record added to subdomain provisioning for inbound email
6 new endpoints: inbox create/list/delete, messages list/read/delete
Plus subdomain/update for catch-all configuration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
x402email Ready Ready Preview, Comment Feb 13, 2026 3:02am

Comment on lines +290 to +291
const subdomain = await prisma.subdomain.findUnique({
where: { name: subdomainName },
Copy link
Copy Markdown

@vercel vercel bot Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subdomain catch-all forwarding doesn't verify DNS status, inconsistent with specific inbox routing which requires dnsVerified

Fix on Vercel

{ status: 409 },
);
}

Copy link
Copy Markdown

@vercel vercel bot Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subdomain inbox creation now allows retainMessages and forwardTo to be enabled independently

Fix on Vercel

- Convert inbox/create from free SIWX → x402 $0.25 payment
- Forward handler skips retention when inbox hits 500 messages
- Messages list and read endpoints return messageCount/messageLimit
  with warning when at >=80% or 100% capacity
- Updated discovery + llms.txt with new pricing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oint

- Move 500 message cap + 100 inbox cap to SUBDOMAIN_INBOX_LIMITS in pricing.ts
- Add POST /api/subdomain/inbox/update (SIWX, free) — change forwardTo
  and retainMessages after creation
- Updated discovery + llms.txt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Now that subdomain messages can share S3 keys with root inbox messages,
the root delete path must check both tables before removing the S3 object.
Also interpolate limits in create endpoint description string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sragss sragss merged commit 4ee8578 into main Feb 13, 2026
4 checks passed
@sragss sragss deleted the feat/subdomain-inboxes branch February 13, 2026 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant