API Reference

HTTP surface for chat streaming, uploads, billing webhooks, and admin utilities. Most UI still uses Server Actions; these routes exist for streaming, files, and providers that need raw HTTP.

Authentication

All dashboard API routes require authentication via Supabase session cookies.

Chat API

GET /api/chat

Health check endpoint.

Response:

{ "message": "Chat API is working" }

POST /api/chat

Send a message and receive AI response.

Request Body:

{
  messages: Array<{
    role: "user" | "assistant" | "system";
    content: string;
  }>;
  chatId?: string;           // Existing chat UUID (optional)
  modelId?:                 // AI model (default: "gpt-4o-mini")
    | "gpt-4o-mini"
    | "gpt-4o"
    | "gpt-4-turbo"
    | "claude-3-5-sonnet-20241022"
    | "claude-3-opus-20240229";
  documentIds?: string[];    // Document UUIDs for RAG context (optional)
}

Response:

  • 200: Streaming text response
  • 401: Unauthorized
  • 402: Insufficient credits
  • 403: Model not allowed for tier
  • 422: Validation error (invalid body)
  • 500: Server error

Headers:

  • x-chat-id: Chat ID (returned for new chats)

Example:

const response = await fetch("/api/chat", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    messages: [{ role: "user", content: "Hello!" }],
    modelId: "gpt-4o-mini",
  }),
});

const reader = response.body.getReader();
// Read streaming response...

Upload API

POST /api/upload

Upload a document for RAG processing.

Request Body: FormData

  • file: File (allowed types and max size are enforced by src/config/rag.ts)

Response:

{ "documentId": "uuid", "filename": "my.pdf", "status": "processing" }

Status Codes:

  • 200: Success
  • 400: Invalid file type or size
  • 401: Unauthorized
  • 500: Upload failed

Example:

const formData = new FormData();
formData.append("file", file);

const response = await fetch("/api/upload", {
  method: "POST",
  body: formData,
});

const { documentId } = await response.json();

RAG API

POST /api/rag

Trigger document vectorization.

Request Body:

{
  documentId: string;
}

Response:

{ "success": true, "message": "Document processing started" }

Status Codes:

  • 200: Vectorization started (async)
  • 400: Invalid document ID
  • 401: Unauthorized
  • 403: Forbidden (document does not belong to user)
  • 404: Document not found
  • 500: Vectorization failed

Billing API

POST /api/billing/portal

Create a Stripe Billing Portal session and return a redirect URL.

Response:

{ "url": "https://billing.stripe.com/session/..." }

GET /api/billing/invoices

Fetch user's Stripe invoices.

Query Parameters:

  • customerId: Stripe customer ID

Response:

{
  invoices: Array<{
    id: string;
    amount: number;
    currency: string;
    status: string | null;
    date: string;
    invoiceUrl: string | null;
    description: string | null;
  }>;
}

GET /api/billing/credit-history

Fetch the user's credit transaction history.

Query Parameters:

  • limit (optional, default 50)
  • offset (optional, default 0)
  • type (optional): filter by transaction_type (e.g. usage, reset, topup)

Response:

{
  transactions: any[];
  total: number;
  limit: number;
  offset: number;
}

Webhook API

POST /api/webhooks/stripe

Handle Stripe webhook events.

Headers:

  • stripe-signature: Stripe signature header

Handled Events:

  • checkout.session.completed: Upgrade/top-up success
  • invoice.paid: Monthly credit renewal (subscription cycles)
  • customer.subscription.deleted: Downgrade to free

Response:

  • 200: { received: true }
  • 400: Invalid signature

Server Actions

generateCheckoutLink

Create Stripe checkout session.

Location: src/app/actions/stripe.ts

import { generateCheckoutLink } from "@/app/actions/stripe";

const url = await generateCheckoutLink(
  priceId,           // Stripe price ID
  "subscription"     // "subscription" | "one-time"
);
// Redirect to url

signOut

Sign out user.

Location: src/app/(auth)/actions/auth.ts

import { signOut } from "@/app/(auth)/actions/auth";

// Use in form action
<form action={signOut}>
  <button type="submit">Logout</button>
</form>

Error Codes

CodeDescription
400Bad Request - Invalid input
401Unauthorized - Not logged in
402Payment Required - Insufficient credits
403Forbidden - Feature not available for tier
404Not Found - Resource doesn't exist
500Server Error - Internal error

Cron API

GET /api/cron/reset-credits

Admin route used by the scheduler defined in vercel.json to reset monthly credits.

Auth:

  • Authorization: Bearer <CRON_SECRET>

Rate Limiting

Rate limiting is not enforced by default. A helper exists in src/lib/rate-limit/, but it is not wired into the API routes yet. Consider adding:

  • Per-user limits via Supabase RLS
  • API route middleware with Upstash Ratelimit
  • Cloudflare Workers for edge rate limiting

Cron API

GET /api/cron/reset-credits

Monthly credit reset job. Intended to be triggered by a scheduler (e.g. Vercel Cron).

Auth:

  • Authorization: Bearer <CRON_SECRET> (recommended)

Response:

{ "message": "Credit reset completed", "resetCount": 3 }

CORS

API routes are same-origin only. For external access:

  1. Add CORS headers to route handlers
  2. Configure next.config.ts headers
  3. Use API keys instead of cookies