HyperSaaS
FrontendRouting

API Routes

Next.js API route handlers that proxy requests to the Django backend.

API routes in src/app/api/ act as a proxy layer between the frontend client components and the Django backend. They handle JWT token injection, request validation, and response forwarding.

Proxy Pattern

Every API route follows the same pattern:

export async function POST(req: Request, { params }: { params: Promise<{ workspaceId: string }> }) {
  // 1. Extract JWT from NextAuth session
  const token = await getAccessToken();
  if (!token) return Response.json({ error: "Unauthorized" }, { status: 401 });

  // 2. Parse and validate request body
  const body = await req.json();

  // 3. Forward to Django backend
  const { workspaceId } = await params;
  const res = await fetch(
    `${BACKEND_URL}/api/workspaces/${workspaceId}/chats/`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `JWT ${token}`,
      },
      body: JSON.stringify(body),
    }
  );

  // 4. Forward response
  const data = await res.json();
  return Response.json(data, { status: res.status });
}

Route Categories

Auth Routes (api/auth/)

RouteMethodBackend Endpoint
auth/[...nextauth]GET/POSTNextAuth.js handler
auth/registerPOST/auth/users/
auth/activate-userPOST/auth/users/activation/
auth/resend-activationPOST/auth/users/resend_activation/
auth/reset-passwordPOST/auth/users/reset_password/
auth/password-reset-confirmPOST/auth/users/reset_password_confirm/
auth/reset-usernamePOST/auth/users/reset_email/
auth/username-reset-confirmPOST/auth/users/reset_email_confirm/
auth/confirm-email-changePOST/auth/users/confirm_email_change/

Workspace Routes (api/workspaces/)

RouteMethodsBackend Endpoint
workspaces/GET, POST/api/workspaces/
workspaces/[id]PATCH, DELETE/api/workspaces/{id}/ (rename, archive or restore; delete)
workspaces/[id]/planGET/api/workspaces/{id}/plan/
workspaces/[id]/ai-modelsGET/api/workspaces/{id}/ai-models/
workspaces/[id]/transfer-ownershipPOST/api/workspaces/{id}/transfer-ownership/
workspaces/[id]/leavePOST/api/workspaces/{id}/leave/
workspaces/[id]/memberships/[membershipId]PATCH, DELETE/api/workspacememberships/{id}/ (role; remove)
workspaces/[id]/invitationsGET, POST/api/invitations/list_by_target/?workspace_id=, /api/invitations/
workspaces/[id]/invitations/[invitationId]PATCH, DELETE/api/invitations/{id}/ (role; revoke)
workspaces/[id]/invitations/[invitationId]/resendPOST/api/invitations/{id}/resend/

Account Routes (api/account/)

RouteMethodsBackend Endpoint
accountPATCH, DELETE/auth/users/me/ (your name; deleting your account, with your password)
account/passwordPOST/auth/users/set_password/
account/emailPOST/auth/users/reset_email/, for your own address

Chat Routes (api/workspaces/[id]/chats/)

RouteMethodsBackend Endpoint
chats/GET, POST/api/workspaces/{ws}/chats/
chats/[chatId]GET, PATCH, DELETE/api/workspaces/{ws}/chats/{id}/ (PATCH sends only the settings that changed)
chats/[chatId]/messagesPOST/api/workspaces/{ws}/chats/{id}/messages/
chats/[chatId]/messages/regeneratePOST.../messages/regenerate/
chats/[chatId]/toggle-publicPOST.../toggle-public/
chats/[chatId]/invite-userPOST.../share/
chats/[chatId]/knowledge-basesGET.../knowledge-bases/
chats/[chatId]/knowledge-bases/attachPOST.../knowledge-bases/attach/
chats/[chatId]/knowledge-bases/detachPOST.../knowledge-bases/detach/

Document Routes (api/workspaces/[id]/documents/)

RouteMethodsBackend Endpoint
documents/GET/api/workspaces/{ws}/documents/
documents/uploadPOST/api/workspaces/{ws}/documents/upload/
documents/from-urlPOST/api/workspaces/{ws}/documents/from-url/
documents/[docId]GET, PUT, DELETE/api/workspaces/{ws}/documents/{id}/
documents/[docId]/confirm-uploadPOST.../confirm-upload/
documents/[docId]/download-urlGET.../download-url/
documents/[docId]/processing-statusGET.../processing-status/
documents/[docId]/reprocessPOST.../reprocess/
documents/[docId]/chunksGET.../chunks/
documents/[docId]/contentGET.../content/

Knowledge Base Routes (api/workspaces/[id]/knowledge-bases/)

RouteMethodsBackend Endpoint
knowledge-bases/GET, POST/api/workspaces/{ws}/knowledge-bases/
knowledge-bases/[kbId]GET, PUT, DELETE.../knowledge-bases/{id}/
knowledge-bases/[kbId]/documentsGET.../documents/
knowledge-bases/[kbId]/add-documentsPOST.../add-documents/
knowledge-bases/[kbId]/remove-documentsPOST.../remove-documents/

Team Routes (api/workspaces/[id]/teams/)

RouteMethodsBackend Endpoint
teams/POST/api/workspaces/{ws}/teams/
teams/[teamId]GET, PUT, DELETE.../teams/{id}/
teams/[teamId]/invitationsGET, POST.../invitations/
teams/[teamId]/memberships/[id]PUT, DELETE.../memberships/{id}/
teams/[teamId]/leavePOST.../leave/

Subscription Routes (api/subscriptions/)

RouteMethodsBackend Endpoint
subscriptions/checkoutPOST/api/subscriptions/checkout/ (price_id, workspace_id)
subscriptions/create-portal-linkPOST/api/subscriptions/customer-portal/ (workspace_id)

Stripe's webhook goes straight to the backend (/api/subscriptions/webhook), not through the frontend.

Utility Routes

RouteMethodsDescription
transcribe-audioPOSTForwards audio file to backend Whisper endpoint
send-contact-emailPOSTSends contact form via SendGrid
workspaces/[id]/translatePOSTText translation via AI provider

Error Handling

The routes for workspaces, settings, billing and chat settings pass on the backend's own status and body through one helper, so its messages ("Billing for this workspace is managed by …", "The Free plan doesn't include …") reach the page unchanged:

// lib/api/backend.ts
export async function relayToBackend(path: string, init: RequestInit = {}) {
  const response = await backendFetch(path, init); // as the signed-in user
  if (!response) return NextResponse.json({ detail: "You're signed out. Sign in again." }, { status: 401 });
  if (response.status === 204) return new NextResponse(null, { status: 204 });
  return NextResponse.json(await response.json(), { status: response.status });
}

IDs from the URL are checked to be UUIDs before they go into a backend path. In the browser, callApi() (lib/api/client.ts) turns a DRF error body into one readable sentence, and pages show it in a Sonner toast or next to the field it's about.

On this page