API Authentication Enforcement
Every API endpoint follows a strict authentication and authorization pattern.
Auth Matrix
| Endpoint | Auth Level | Token Cost | Notes |
|---|---|---|---|
GET /api/track-pixel | None | None | Public tracking pixel |
GET /api/blog-og | None | None | Public OG image |
POST /api/generate | User | ai_generation | Main RAG pipeline |
POST /api/generate-email | User | ai_generation | Simple generation |
POST /api/send-email | User | email_send | Resend delivery |
POST /api/send-email-ses | User | email_send | SES delivery |
POST /api/validate-amp | User | None | AMP validation |
GET /api/resend-status | User | None | Delivery status |
POST /api/admin/* | Admin | None | RAG management |
GET /api/admin/* | Admin | None | Admin data |
Enforcement Pattern
Every protected endpoint follows this pattern at the top of the handler:
export default async function handler(req, res) {
// 1. Method check
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
// 2. Auth verification
const user = await verifyUser(req, res);
if (!user) return; // 401 already sent
// 3. Token spending (if applicable)
const tokenResult = await spendTokensServer(req, res, 'ai_generation');
if (!tokenResult) return; // 402 already sent
// 4. Input validation
const { prompt } = req.body;
if (!prompt) {
return res.status(400).json({ error: 'Prompt is required' });
}
// 5. Business logic...
}
Auth Modules
Two auth modules exist for historical reasons (TypeScript vs JavaScript routes):
| Module | Language | Functions |
|---|---|---|
api/_auth.ts | TypeScript | verifyUser, verifyAdmin, spendTokensServer, setCorsHeaders |
api/admin/_auth.js | JavaScript | Same functions (JS implementation) |
Both implement identical logic. The JS version is used by generate.js and all api/admin/*.js routes.
Token Spending Security
Token deduction is server-side only. The flow:
- Frontend calls
checkCanAfford()— read-only, no deduction - Frontend executes the API call if affordable
- Server calls
spendTokensServer()— atomic deduction viaspend_tokensRPC - Frontend calls
refreshBalance()to sync UI
This ensures users cannot bypass token costs by calling APIs directly (e.g., via curl).