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.
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 Method Backend Endpoint auth/[...nextauth]GET/POST NextAuth.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/
Route Methods Backend 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/
Route Methods Backend 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
Route Methods Backend 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/
Route Methods Backend 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/
Route Methods Backend 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/
Route Methods Backend 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/
Route Methods Backend 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.
Route Methods Description transcribe-audioPOST Forwards audio file to backend Whisper endpoint send-contact-emailPOST Sends contact form via SendGrid workspaces/[id]/translatePOST Text translation via AI provider
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.