# POST /auth/register

> Create a new user in your Aerostack project

Create a new user in your project. This endpoint allows new users to sign up with **email and password** (password required).

  **Password sign-in:** This flow requires a password. For passwordless (no password field), use [OTP Send](/reference/authentication/otp-send) + [OTP Verify](/reference/authentication/otp-verify) instead.

  **Authentication:** This endpoint does not require authentication. It's a public endpoint.

## Endpoint

```http
POST /v1/public/projects/:projectSlug/auth/register
```

## Request Parameters

### Path Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `projectSlug` | string | ✅ | Your project's unique slug (found in admin dashboard) |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `email` | string | ✅ | User's email address (valid format) |
| `password` | string | ✅ | Strong password (8+ chars, mixed case + number) |
| `name` | string | ❌ | User's display name |
| `customFields` | object | Depends* | Keys must match **Dashboard → Project → Auth → Custom Registration Fields**. Prebuilt (e.g. `phone`) and custom keys go here. |
| `turnstileToken` | string | ❌ | Turnstile token for captcha (if enabled) |

\* Each custom field has its own **required** flag in admin. Required custom fields must be present or the API returns 400.

### Prebuilt and Custom Fields

**Prebuilt fields (system-recognized):**
- `phone` – Add a custom field with key `phone` in admin to collect during signup. Send as `customFields.phone`. Stored in profile (returned by [GET /auth/me](/reference/authentication/me)). The dedicated `users.phone` column is used only for Phone OTP signups.

**Custom fields:** Any other keys you configure (e.g. `company`, `role`). Stored in `profile_extras`.

**Important:** Only fields you add in **Dashboard → Project → Auth → Custom Registration Fields** are accepted. Unconfigured keys in `customFields` are ignored.

| Field | Required | Configured In |
|-------|----------|----------------|
| email | Yes | (always) |
| password | Yes | (always) |
| name | No | (always) |
| customFields.* | Per field | Auth → Custom Registration Fields (key + required toggle) |

See [Configuration scenarios](/reference/authentication/configuration-scenarios) for per-method examples (Email only, Phone only, Both).

### Example Request Body

```json
{
  "email": "user@example.com",
  "password": "StrongPassword123!",
  "name": "Jane Doe",
  "customFields": {
    "company": "Acme Inc",
    "phone": "+1-555-1234"
  }
}
```

Add `phone` and `company` in Auth → Custom Registration Fields first. Mark **Required** for any field that must be present.

## Response

### Success (200 OK)

**When email verification is NOT required** (or user already verified):

```json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "user-uuid-here",
    "email": "user@example.com",
    "name": "Jane Doe"
  },
  "requiresVerification": false
}
```

**When email verification IS required** (project has "Require Email Verification" enabled):

```json
{
  "requiresVerification": true,
  "message": "Please check your email to verify your account before signing in.",
  "user": {
    "id": "user-uuid-here",
    "email": "user@example.com",
    "name": "Jane Doe"
  }
}
```

No `token` is returned until the user verifies their email. Use [GET /auth/verify-email](/reference/authentication/verify-email) (link in email) then [POST /auth/login](/reference/authentication/login) or [POST /auth/otp/verify](/reference/authentication/otp-verify).

### Error Responses

| Status Code | Error Code | Description |
|-------------|------------|-------------|
| 400 | `INVALID_REQUEST` | Validation failed (weak password, invalid email, missing required fields) |
| 409 | `EMAIL_ALREADY_IN_USE` | An account with this email already exists |
| 429 | `RATE_LIMIT_EXCEEDED` | Too many signup requests from this IP |
| 500 | `INTERNAL_SERVER_ERROR` | Server error occurred |

### Example Error Response

```json
{
  "error": "EMAIL_ALREADY_IN_USE",
  "message": "An account with this email already exists",
  "details": {
    "email": "user@example.com"
  }
}
```

## Available Hooks

This endpoint can trigger the following project-specific hooks:

### Lifecycle Hooks

- **`beforeSignup`**
  - **When**: After base validation, before user is created in database
  - **Can do**: Enforce extra validation (e.g., only allow `@company.com` emails), modify custom fields, throw errors to reject signup with custom message
  
- **`afterSignup`**
  - **When**: After user is successfully created (and after verification email is queued if enabled)
  - **Can do**: Send welcome emails, sync to CRM, create related records, enqueue background jobs

### Webhook Events

- **Event**: `auth.signup`
  - **When**: After successful user creation
  - **Can do**: Call external webhooks (HTTP endpoints you host), run script hooks with the Hook SDK

[Learn more about hooks →](/features/hooks)

## SDK Examples

### JavaScript / TypeScript

```javascript
const response = await fetch(
  'https://api.aerostack.dev/v1/public/projects/your-project/auth/register',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email: 'user@example.com',
      password: 'StrongPassword123!',
      name: 'Jane Doe'
    })
  }
);

if (!response.ok) {
  const error = await response.json();
  console.error('Signup failed:', error);
  throw new Error(error.message);
}

const { user, token } = await response.json();
console.log('User created:', user);

// Store token for authenticated requests
localStorage.setItem('authToken', token);
```

### React

```jsx

function SignupForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [name, setName] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSignup = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);

    try {
      const res = await fetch('https://api.aerostack.dev/v1/public/projects/your-project/auth/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password, name })
      });

      if (!res.ok) {
        const errorData = await res.json();
        throw new Error(errorData.message);
      }

      const { user, token } = await res.json();
      
      // Store token
      localStorage.setItem('authToken', token);
      
      // Redirect or update UI
      window.location.href = '/dashboard';
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSignup} className="space-y-4">
      {error && (
        <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
          {error}
        </div>
      )}
      
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
        className="w-full px-4 py-2 border rounded"
      />
      
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        required
        minLength={8}
        className="w-full px-4 py-2 border rounded"
      />
      
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name (optional)"
        className="w-full px-4 py-2 border rounded"
      />
      
      <button
        type="submit"
        disabled={loading}
        className="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded disabled:opacity-50"
      >
        {loading ? 'Signing up...' : 'Sign Up'}
      </button>
    </form>
  );
}
```

### React Native

```jsx

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [name, setName] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSignup = async () => {
    setLoading(true);
    
    try {
      const response = await fetch(
        'https://api.aerostack.dev/v1/public/projects/your-project/auth/register',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ email, password, name })
        }
      );

      const data = await response.json();

      if (!response.ok) {
        throw new Error(data.message || 'Signup failed');
      }

      const { user, token } = data;
      
      // Store token securely
      await AsyncStorage.setItem('authToken', token);
      
      Alert.alert('Success', 'Account created successfully!');
      navigation.navigate('Dashboard');
    } catch (error) {
      Alert.alert('Error', error.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    
      
      
      
      
      
      
      {loading ? (
        
      ) : (
        
      )}
    
  );
}
```

## Best Practices

  **Security:** Always use HTTPS in production. Never expose API keys or sensitive tokens in client-side code.

✅ **Validate input** on both client and server side  
✅ **Use strong passwords** - enforce minimum 8 characters with mixed case, numbers, and special characters  
✅ **Handle errors gracefully** - show user-friendly error messages  
✅ **Store tokens securely** - use httpOnly cookies for web or secure storage for mobile  
✅ **Implement rate limiting** on your frontend to prevent abuse  
✅ **Consider email verification** - enable in project settings for added security

## Related Endpoints

- [POST /auth/login](/reference/authentication/login) - Authenticate existing users
- [GET /auth/me](/reference/authentication/me) - Get current user information
- [POST /auth/resend-verification](/reference/authentication/resend-verification) - Resend verification email
- [POST /auth/reset-password-request](/reference/authentication/reset-password-request) - Request password reset

## Need Help?

- **📖 Quick Start Guide**: [Get started in 5 minutes](/getting-started/quick-start)
- **💬 Discord**: [Join our community](https://discord.gg/DhVCXg5apS)
- **📧 Support**: [support@aerostack.dev](mailto:support@aerostack.dev)
