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:
| Endpoint | Purpose |
|---|---|
POST /api/auth/sign-up/email | Register with email/password |
POST /api/auth/sign-in/email | Login with email/password |
POST /api/auth/sign-in/social | OAuth login (Google, GitHub) |
POST /api/auth/sign-in/magic-link | Send magic link email |
GET /api/auth/session | Get current session |
POST /api/auth/sign-out | Sign out and invalidate session |
GET /api/auth/callback/:provider | OAuth 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:
| Status | Body | Reason |
|---|---|---|
| 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:
| Event | Action |
|---|---|
payment.completed | Create or activate subscription record |
subscription.updated | Update plan or billing status |
subscription.cancelled | Mark subscription as cancelled |
Response:
{ "received": true }
Errors:
| Status | Body | Reason |
|---|---|---|
| 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:
| Field | Rules |
|---|---|
name | Optional. String, 1-100 characters. |
avatar | Optional. Valid URL string. |
Errors:
| Status | Body | Reason |
|---|---|---|
| 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:
| Status | Body | Reason |
|---|---|---|
| 401 | Unauthorized | No valid session |
| 429 | Rate limit exceeded | Monthly token limit reached |
| 500 | AI service error | Provider 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— returnstext/event-stream(Server-Sent Events)/api/webhook/dodo— acceptsapplication/json, returns minimal confirmation