Directory Structure

sendoka/
├── proxy.ts                      # Dashboard auth gate (renamed from middleware.ts in Next 16)
├── next.config.ts
├── drizzle.config.ts
├── vercel.ts                     # Cron registration (typed Vercel config)
├── vitest.config.ts              # Test config
├── .env.example
├── AGENTS.md / CLAUDE.md
├── public/
├── docs/
└── src/
    ├── app/
    │   ├── layout.tsx
    │   ├── page.tsx              # Marketing landing
    │   ├── providers.tsx         # SessionProvider + ToastProvider
    │   ├── globals.css
    │   ├── (auth)/               # Unauthenticated route group
    │   │   ├── login/page.tsx
    │   │   ├── register/page.tsx
    │   │   ├── forgot-password/page.tsx
    │   │   └── reset-password/page.tsx
    │   ├── invite/page.tsx       # Team invite acceptance
    │   ├── docs/page.tsx         # Public API reference
    │   ├── dashboard/
    │   │   ├── layout.tsx
    │   │   ├── page.tsx
    │   │   ├── messages/page.tsx
    │   │   ├── api-keys/page.tsx
    │   │   ├── domains/page.tsx
    │   │   ├── webhooks/page.tsx
    │   │   └── settings/
    │   │       ├── page.tsx
    │   │       ├── billing/page.tsx
    │   │       └── team/
    │   │           ├── page.tsx
    │   │           └── invites.tsx     # client component
    │   └── api/
    │       ├── auth/
    │       │   ├── [...nextauth]/route.ts
    │       │   ├── register/route.ts
    │       │   ├── verify-email/route.ts
    │       │   ├── forgot-password/route.ts
    │       │   └── reset-password/route.ts
    │       ├── internal/         # Session-auth'd
    │       │   ├── api-keys/route.ts
    │       │   ├── billing/route.ts
    │       │   ├── domains/route.ts
    │       │   ├── messages/route.ts
    │       │   ├── webhooks/route.ts
    │       │   ├── webhooks/rotate/route.ts
    │       │   ├── webhook-deliveries/route.ts
    │       │   ├── templates/route.ts
    │       │   ├── team/invite/route.ts
    │       │   ├── team/accept/route.ts
    │       │   ├── org/route.ts
    │       │   ├── org/switch/route.ts
    │       │   ├── audit/route.ts
    │       │   └── two-factor/
    │       │       ├── setup/route.ts
    │       │       ├── verify/route.ts
    │       │       └── disable/route.ts
    │       ├── v1/               # Public API
    │       │   ├── emails/
    │       │   │   ├── route.ts
    │       │   │   ├── batch/route.ts
    │       │   │   └── [emailId]/route.ts
    │       │   └── sms/
    │       │       ├── route.ts
    │       │       ├── batch/route.ts
    │       │       └── [smsId]/route.ts
    │       ├── webhooks/
    │       │   ├── ses/route.ts           # SES events via SNS
    │       │   ├── sns-sms/route.ts       # SMS delivery status
    │       │   └── stripe/route.ts
    │       └── cron/
    │           ├── send-scheduled/route.ts
    │           ├── retry-webhooks/route.ts
    │           ├── cleanup-idempotency/route.ts
    │           └── report-overage/route.ts
    ├── components/
    │   ├── dashboard/
    │   │   ├── upgrade-button.tsx
    │   │   └── org-switcher.tsx
    │   └── ui/
    │       ├── toast.tsx
    │       ├── skeleton.tsx
    │       ├── confirm.tsx
    │       └── error-boundary.tsx
    └── lib/
        ├── db/
        │   ├── index.ts                       # drizzle + raw sql client
        │   ├── migrations/                    # generated SQL
        │   └── schema/
        │       ├── index.ts                   # barrel — 16 tables
        │       ├── users.ts
        │       ├── organizations.ts
        │       ├── api-keys.ts
        │       ├── messages.ts                # +scheduled_at
        │       ├── domains.ts
        │       ├── webhooks.ts                # +previous_secret
        │       ├── webhook-deliveries.ts
        │       ├── usage.ts
        │       ├── idempotency.ts
        │       ├── audit.ts
        │       ├── email-verifications.ts
        │       ├── password-resets.ts
        │       ├── team-invites.ts
        │       ├── templates.ts
        │       └── two-factor.ts
        ├── auth/
        │   ├── options.ts                     # NextAuth + Google/GitHub + TOTP
        │   ├── api-key.ts
        │   ├── active-org.ts                  # cookie-based org switcher
        │   ├── email-verification.ts
        │   └── totp.ts
        ├── api/
        │   ├── middleware.ts                  # withApiAuth
        │   ├── errors.ts
        │   ├── validation.ts                  # +scheduled_at, attachments, template
        │   ├── rate-limit.ts                  # per-plan
        │   ├── idempotency.ts
        │   ├── usage.ts                       # accepts `by` count
        │   ├── usage-limit.ts
        │   ├── webhook-fanout.ts              # delivery log + retry
        │   ├── sns-verify.ts                  # SNS signature check
        │   ├── cursor.ts                      # compound-key pagination
        │   ├── audit.ts                       # logAudit helper
        │   └── template.ts                    # {{var}} substitution
        ├── billing/
        │   ├── stripe.ts
        │   └── plans.ts
        ├── providers/
        │   ├── types.ts                       # EmailAttachment added
        │   ├── email.ts                       # raw MIME + attachments
        │   ├── sms.ts
        │   └── domain.ts
        └── utils/
            ├── id.ts
            └── crypto.ts

Tests: co-located `*.test.ts` files in src/lib/**.