---
name: payload-v3-admin-init
description: Create the first admin user in Payload CMS v3 via an internal API route. Solves the missing onInit hook problem.
category: devops
---

# Payload v3 — Create Admin User via API Route

## Problem
No admin user exists in Payload CMS. Login page at `/admin` shows email/password form but no user was created on first boot.

## Key Finding: No `onInit` Hook in Payload v3
Payload v3 `buildConfig()` does NOT have an `onInit` hook. The v2 pattern `hooks: { init: [...] }` does not exist. Adding it causes TypeScript errors.

## Solution: Create Admin via API Route

**File:** `src/app/api/create-admin/route.ts`

```typescript
import { NextResponse } from 'next/server'
import { getPayload } from 'payload'
import config from '@/payload.config'

export async function POST() {
  try {
    const p = await getPayload({ config })

    const existing = await p.find({ collection: 'users', limit: 1 })
    if (existing.totalDocs > 0) {
      return NextResponse.json({ message: 'Admin already exists', email: existing.docs[0].email })
    }

    const result = await p.create({
      collection: 'users',
      data: {
        email: 'admin@dealplustech.co.th',
        password: 'DealPlus2026!',
      },
    })

    return NextResponse.json({ success: true, email: result.email })
  } catch (err: any) {
    return NextResponse.json({ error: err.message }, { status: 500 })
  }
}
```

Then call:
```bash
curl -X POST http://localhost:3001/api/create-admin
```

## Common Errors

| Error | Cause | Fix |
|-------|-------|-----|
| `the payload config is required for getPayload to work` | Used `getPayload({ mongoURL })` instead of `getPayload({ config })` | Pass `config` import |
| `GET /api/users` returns 403 | Auth required — cannot list users without being logged in | Use internal API route instead |
| `onInit` in `buildConfig()` TypeScript error | Hook doesn't exist in v3 | Remove it, use API route |

## Verification
After creating, visit `/admin` and login with the credentials set in the API route.
