KitRocket

API Routes

Complete reference for all API endpoints in the KitRocket boilerplate.

All API routes live in src/app/api/. Routes that require authentication are marked with a lock icon.

Authentication

POST /api/auth/*

Better Auth handles all authentication routes through a catch-all handler at src/app/api/auth/[...all]/route.ts.

Key endpoints managed by Better Auth:

EndpointPurpose
POST /api/auth/sign-up/emailRegister with email/password
POST /api/auth/sign-in/emailLogin with email/password
POST /api/auth/sign-in/socialOAuth login (Google, GitHub)
POST /api/auth/sign-in/magic-linkSend magic link email
GET /api/auth/sessionGet current session
POST /api/auth/sign-outSign out and invalidate session
GET /api/auth/callback/:providerOAuth callback handler

Sign up with email

curl -X POST http://localhost:3000/api/auth/sign-up/email \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "securepassword123",
    "name": "Jane Smith"
  }'

Response:

{
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Smith"
  },
  "session": {
    "id": "ses_xyz789",
    "token": "eyJhbGciOiJ..."
  }
}

Sign in with email

curl -X POST http://localhost:3000/api/auth/sign-in/email \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "securepassword123"
  }'

Response:

{
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Smith"
  },
  "session": {
    "id": "ses_new456",
    "token": "eyJhbGciOiJ..."
  }
}

Get current session

curl http://localhost:3000/api/auth/session \
  -H "Cookie: better-auth.session_token=eyJhbGciOiJ..."

Response:

{
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "Jane Smith",
    "image": null
  },
  "session": {
    "id": "ses_xyz789",
    "expiresAt": "2026-04-17T00:00:00.000Z"
  }
}

Returns null if no valid session exists.

OAuth sign in

curl -X POST http://localhost:3000/api/auth/sign-in/social \
  -H "Content-Type: application/json" \
  -d '{ "provider": "google" }'

Response:

{
  "url": "https://accounts.google.com/o/oauth2/v2/auth?..."
}

Redirect the user to this URL. After authorization, they're redirected back to your app via the callback handler.

Payments

POST /api/checkout 🔒

Create a DodoPayments checkout session.

File: src/app/api/checkout/route.ts

Request:

curl -X POST http://localhost:3000/api/checkout \
  -H "Content-Type: application/json" \
  -H "Cookie: better-auth.session_token=..." \
  -d '{ "priceId": "price_starter_monthly" }'

Response:

{
  "url": "https://checkout.dodopayments.com/cs_abc123"
}

Errors:

StatusBodyReason
401{ "error": "Unauthorized" }No valid session
400{ "error": "Invalid price ID" }Price ID not recognized
500{ "error": "Failed to create checkout" }DodoPayments API error

POST /api/webhook/dodo

Handle DodoPayments webhook events. This route does not require session authentication — it verifies the webhook signature instead.

File: src/app/api/webhook/dodo/route.ts

Headers required:

x-dodo-signature: webhook_signature_here
Content-Type: application/json

Handled events:

EventAction
payment.completedCreate or activate subscription record
subscription.updatedUpdate plan or billing status
subscription.cancelledMark subscription as cancelled

Response:

{ "received": true }

Errors:

StatusBodyReason
400{ "error": "Invalid signature" }Webhook signature verification failed
400{ "error": "Missing signature" }No signature header present

User Profile

GET /api/user/profile 🔒

Get the authenticated user's profile.

File: src/app/api/user/profile/route.ts

Request:

curl http://localhost:3000/api/user/profile \
  -H "Cookie: better-auth.session_token=..."

Response:

{
  "id": "usr_abc123",
  "email": "user@example.com",
  "name": "Jane Smith",
  "avatar": "https://example.com/avatar.jpg",
  "createdAt": "2026-01-15T10:30:00.000Z"
}

PATCH /api/user/profile 🔒

Update the authenticated user's profile.

File: src/app/api/user/profile/route.ts

Request:

curl -X PATCH http://localhost:3000/api/user/profile \
  -H "Content-Type: application/json" \
  -H "Cookie: better-auth.session_token=..." \
  -d '{ "name": "Jane Doe", "avatar": "https://example.com/new-avatar.jpg" }'

Response:

{
  "id": "usr_abc123",
  "email": "user@example.com",
  "name": "Jane Doe",
  "avatar": "https://example.com/new-avatar.jpg",
  "createdAt": "2026-01-15T10:30:00.000Z"
}

Validation:

FieldRules
nameOptional. String, 1-100 characters.
avatarOptional. Valid URL string.

Errors:

StatusBodyReason
401{ "error": "Unauthorized" }No valid session
400{ "error": "Validation failed" }Invalid input data

AI Chat (Pro)

POST /api/ai/chat 🔒

Send messages and receive a streaming AI response.

File: src/app/api/ai/chat/route.ts

Request:

curl -X POST http://localhost:3000/api/ai/chat \
  -H "Content-Type: application/json" \
  -H "Cookie: better-auth.session_token=..." \
  -d '{
    "messages": [
      { "role": "user", "content": "Explain how webhooks work." }
    ]
  }'

Response: Server-Sent Events stream

data: {"type":"text","content":"Webhooks are"}
data: {"type":"text","content":" HTTP callbacks"}
data: {"type":"text","content":" that notify your"}
data: {"type":"text","content":" application when"}
data: {"type":"text","content":" events occur..."}
data: {"type":"done","usage":{"inputTokens":12,"outputTokens":156,"model":"claude-sonnet-4-20250514"}}

Errors:

StatusBodyReason
401UnauthorizedNo valid session
429Rate limit exceededMonthly token limit reached
500AI service errorProvider API error

Common patterns

Authentication check

All protected routes follow this pattern:

const session = await auth.api.getSession({
  headers: request.headers,
});

if (!session) {
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

Error response format

All API routes return errors consistently:

{
  "error": "Human-readable error message"
}

Content-Type

All routes accept and return application/json except:

  • /api/ai/chat — returns text/event-stream (Server-Sent Events)
  • /api/webhook/dodo — accepts application/json, returns minimal confirmation

On this page