Loading...
Complete reference for all REST API endpoints in the Cloudless.gr app.
Returns published blog posts from Notion, with static fallback.
Returns a single blog post by slug.
Returns published documentation grouped by category.
Submits a contact form entry to the Notion Submissions database.
All routes in this section require requireAdmin from src/lib/api-auth.ts: Cognito JWT RS256 verification + membership in the admin Cognito group. Unauthenticated requests → 401; authenticated non-admins → 403.
Lists contact form submissions with status filtering.
Lists tasks with optional status/priority/assignee filters.
Lists projects with status filtering.
Cross-database search powered by the Notion Search API.
Returns analytics summary and recent events.
All routes require requireAdmin (Cognito JWT, admin group). Returns 503 if GSC credentials are missing from SSM.
GET /api/admin/analytics/seo28-day aggregate: clicks, impressions, CTR, avg position + top 20 keywords.
GET /api/admin/analytics/keywords?limit=NTop keywords by clicks. limit max 100, default 10.
GET /api/admin/analytics/pages?limit=NTop pages by clicks. limit max 100, default 25.
GET /api/admin/analytics/history?weeks=NDaily performance history for trend charts. weeks max 52. Query param via request.nextUrl.searchParams.
GET /api/admin/analytics/webTotals + top pages combined — acts as a general analytics proxy for the dashboard.
GET /api/admin/analytics/countries?limit=NOrganic traffic breakdown by country. limit clamped 1–50, default 30.
GET /api/admin/ops/errorsReturns the 20 most recent unresolved Sentry issues. Returns 503 if Sentry is not configured (SENTRY_AUTH_TOKEN absent from SSM).
GET /api/admin/usersLists all Cognito users. Each record exposes: username, email, name, company, phone, emailVerified, status (active/disabled), userStatus (Cognito confirmation status), role (admin/user), created, lastModified.
POST /api/admin/usersCreate a new user. Body: { email, name, role }. Does not set a groupName from the body — group assignment is done via separate promote/demote.
PUT /api/admin/users/[id]Promote or demote a user's admin role. Adds or removes from the Cognito admin group.
DELETE /api/admin/users/[id]Disable or delete a user account.
Authenticated user routes use requireAuth (JWT verification, no group requirement).
GET /api/user/purchasesReturns Stripe purchases belonging to the authenticated user.
GET /api/user/consultationsReturns booked Google Calendar consultations for the authenticated user.
Middleware (src/proxy.ts)Runs on every request. Enforces:
• CORS — only cloudless.gr and www.cloudless.gr as allowed origins. Dev: localhost:3000|3001|4000 (exact regex match, not prefix).
• Rate limiting — per-IP sliding window on POST routes (contact: 5/min, subscribe: 3/min, checkout: 10/min, calendar/book: 5/min, hubspot/ticket: 5/min).
• Security headers — X-Frame-Options: DENY, Strict-Transport-Security, Referrer-Policy, Permissions-Policy.
POST /api/contactValidates and sends email via SES, then fires three background tasks in parallel (Promise.allSettled):
1. slackContactNotify — Slack Block Kit message
2. upsertContact — HubSpot CRM create/update
3. saveSubmission — Notion Submissions database
Background failures are logged individually but never bubble up to the API response. The .catch() on the allSettled chain prevents unhandled rejection warnings on Lambda.