API Routes

indiegoon includes pre-built API routes for authentication, billing, teams, and more. Here's the complete reference.

Authentication

All auth endpoints are handled by Better Auth's catch-all route:

/api/auth/[...all]

This automatically provides:

EndpointMethodDescription
/api/auth/sign-up/emailPOSTRegister with email/password
/api/auth/sign-in/emailPOSTSign in with email/password
/api/auth/sign-in/socialPOSTOAuth sign-in (Google, GitHub, etc.)
/api/auth/sign-in/magic-linkPOSTSend magic link email
/api/auth/sign-outPOSTEnd session
/api/auth/get-sessionGETGet current session
/api/auth/forget-passwordPOSTRequest password reset
/api/auth/reset-passwordPOSTSet new password
/api/auth/verify-emailPOSTVerify email token

You never need to modify this route. Better Auth handles everything.

Billing

Create Checkout

POST /api/billing/checkout
Content-Type: application/json

{ "priceId": "price_..." }

→ { "url": "https://checkout.stripe.com/..." }

Creates a checkout session with the configured payment provider. Requires authentication.

Customer Portal

POST /api/billing/portal

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

Returns a URL to the customer's billing portal (manage subscription, update card, view invoices).

List Invoices

GET /api/billing/invoices

→ { "invoices": [{ "id": "...", "amount": 9900, "status": "paid", "date": "..." }] }

Webhooks

Payment Webhooks

POST /api/webhooks/payments

Universal webhook endpoint. Works with all payment providers:

  1. •Reads raw request body
  2. •Verifies HMAC signature (provider-specific)
  3. •Parses event into standard format
  4. •Syncs subscription status to database

Set this URL in your payment provider's webhook settings.

Legacy Stripe Webhook

POST /api/webhooks/stripe

Alias that redirects to /api/webhooks/payments. Exists for backwards compatibility.

Teams

MethodEndpointAuthDescription
GET/api/teamsUserList your teams
POST/api/teamsUserCreate a team
GET/api/teams/[teamId]MemberGet team details + your role
PATCH/api/teams/[teamId]AdminUpdate team name/logo
DELETE/api/teams/[teamId]OwnerDelete team
POST/api/teams/[teamId]/inviteAdminInvite member by email

Create Team

POST /api/teams
Content-Type: application/json

{ "name": "Acme Corp" }

→ { "id": "...", "name": "Acme Corp", "slug": "acme-corp" }

Name must be 2-50 characters. Slug is auto-generated.

Invite Member

POST /api/teams/[teamId]/invite
Content-Type: application/json

{ "email": "new@member.com", "role": "member" }

→ { "invitation": { "id": "...", "token": "...", "expiresAt": "..." } }

Valid roles: admin, member. Owner role cannot be assigned via invitation.

Lead Capture

POST /api/lead
Content-Type: application/json

{ "email": "visitor@example.com", "source": "landing-page" }

→ { "success": true }

Idempotent — won't duplicate if email already exists. Use this for waitlists, newsletter signups, etc.

OG Image Generation

GET /api/og?title=Hello+World

Generates a dynamic Open Graph image using @vercel/og (Edge Runtime). Used automatically by your metadata configuration.

Adding Your Own Routes

Create new routes in src/app/api/:

// src/app/api/projects/route.ts
import { auth } from "@/lib/auth"
import { db } from "@/lib/db"

export async function GET(request: Request) {
  const session = await auth.api.getSession({ headers: request.headers })
  if (!session) {
    return Response.json({ error: "Unauthorized" }, { status: 401 })
  }

  const projects = await db.query.projects.findMany({
    where: eq(projects.userId, session.user.id),
  })

  return Response.json({ projects })
}

export async function POST(request: Request) {
  const session = await auth.api.getSession({ headers: request.headers })
  if (!session) {
    return Response.json({ error: "Unauthorized" }, { status: 401 })
  }

  const body = await request.json()
  
  const project = await db.insert(projects).values({
    id: crypto.randomUUID(),
    name: body.name,
    userId: session.user.id,
  }).returning()

  return Response.json({ project: project[0] }, { status: 201 })
}

Rate Limiting

All API routes (except /api/auth/* and /api/webhooks/*) are rate limited via middleware:

  • •Limit: 20 requests per 10 seconds per IP
  • •Algorithm: Sliding window (Upstash Redis)
  • •Fallback: If Redis is not configured, rate limiting is disabled (requests pass through)

The rate limiter returns 429 Too Many Requests when exceeded.

CORS & Security

  • •API routes are server-side only (no CORS issues for same-origin requests)
  • •Webhook routes skip auth and rate limiting (they verify signatures instead)
  • •All responses include security headers from next.config.ts

Next Steps

  • •Authentication — How auth works under the hood
  • •Payments — Payment provider details
  • •Teams — Team API in depth