Architecture Overview
Sendoka is a single Next.js 16 App Router application. All API surface, dashboard UI, and background handlers run as route handlers in one deployable unit.
Layers
┌─────────────────────────────────────────────────────────┐
│ Dashboard UI (src/app/overview/**) │
│ React Server Components — NextAuth session gated │
└────────────┬────────────────────────────────────────────┘
│ fetch
┌────────────▼────────────────────────────────────────────┐
│ Internal API (src/app/api/internal/**) │
│ Session-auth'd CRUD for dashboard │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Public API v1 (src/app/api/v1/**) │
│ API-key auth'd send endpoints │
└────────────┬────────────────────────────────────────────┘
│
┌────────────▼─────────────┬──────────────────────────────┐
│ withApiAuth middleware │ • Bearer key → org/env │
│ (src/lib/api/middleware) │ • Rate limit (Upstash) │
└────────────┬─────────────┴──────────────────────────────┘
│
┌────────────▼────────────────────────────────────────────┐
│ Providers (src/lib/providers/**) │
│ AWS SES v2 (email) · AWS SNS (SMS) · SES domain ID │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Inbound Webhooks (src/app/api/webhooks/**) │
│ SES → SNS HTTP · Stripe signed events │
└────────────┬────────────────────────────────────────────┘
│
┌────────────▼────────────────────────────────────────────┐
│ Webhook fan-out → customer endpoints │
│ (src/lib/api/webhook-fanout.ts) │
└─────────────────────────────────────────────────────────┘
Data: Neon Postgres (Drizzle ORM). Cache/RL: Upstash Redis.
Key modules
src/app/api/v1/*— public REST API (email/SMS send, list, retrieve, batch).src/app/api/internal/*— session-auth'd dashboard CRUD (keys, domains, webhooks, templates, team, org, billing, audit, two-factor).src/app/api/auth/*— NextAuth + user registration + email verify + password reset.src/app/api/webhooks/*— inbound from SES (status events), SNS SMS (delivery), Stripe.src/app/api/cron/*— scheduled jobs (send-scheduled, retry-webhooks, cleanup-idempotency, report-overage).src/lib/api/*— cross-cutting: auth middleware, errors, idempotency, cursor pagination, rate limit (per-plan), usage metering, webhook fan-out + delivery log, audit log, SNS signature verification, template rendering.src/lib/auth/*— NextAuth options, API-key generate/validate, TOTP, email-verification helper, active-org cookie resolver.src/lib/billing/*— Stripe client + plan definitions.src/lib/providers/*— AWS SES (simple + raw MIME) / SNS wrappers, domain identity management.src/lib/db/*— Drizzle connection + 16 tables.src/components/ui/*— toast, skeleton, confirm dialog, error boundary.
Request authentication modes
| Surface | Auth | Implementation |
|---|---|---|
/overview/* |
NextAuth JWT | middleware.ts |
/api/internal/* |
getServerSession |
route-level checks |
/api/v1/* |
Bearer API key | withApiAuth wrapper |
/api/webhooks/stripe |
Stripe signature | stripe.webhooks.constructEvent |
/api/webhooks/ses |
SNS signature (RSA-SHA1/SHA256 against SigningCertURL) | src/lib/api/sns-verify.ts |
/api/webhooks/sns-sms |
SNS signature | same module |
/api/cron/* |
Bearer CRON_SECRET |
per-handler authorized() |
/api/auth/* |
public (register, NextAuth) | n/a |