Owner: Engineering Team | Last Updated: 2026-01-30 | Status: Current
The WWAI API uses JSON Web Tokens (JWT) for client authentication. This document covers all authentication-related endpoints including user registration, login (email, Google, Facebook), token refresh, account retrieval, and password reset flows.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/user/signup/ |
No | Register a new user account. |
| POST | /api/user/login/ |
No | Authenticate with email and password to receive JWT tokens. |
| POST | /api/user/login/google/ |
No | Authenticate using a Google OAuth token. If the user does no |
| POST | /api/user/login/facebook/ |
No | Authenticate using a Facebook OAuth token. If the user does |
| POST | /api/user/refresh/ |
No | Refresh an expired access token using a valid refresh token. |
| GET | /api/user/account/ |
Yes | Retrieve the authenticated user's profile information. |
| POST | /api/user/forgot-password/ |
No | Request a password reset email. Sends a reset link to the pr |
| POST | /api/user/reset-password/ |
No | Reset the user's password using a token received via the pas |
All authentication endpoints are prefixed with /api/user/.
1. User signs up or logs in
POST /api/user/signup/ OR POST /api/user/login/
--> Receives: { access, refresh } JWT tokens
2. Client stores tokens and sends access token with requests
Authorization: Bearer <access_token>
3. When access token expires, refresh it
POST /api/user/refresh/ with { refresh: <refresh_token> }
--> Receives: { access } new access token
4. If refresh token expires, user must log in again
Register a new user account.
Authentication: None required
curl -X POST "${BASE_URL}/api/user/signup/" \
-H "Content-Type: application/json" \
-d '{
"email": "newuser@example.com",
"password": "SecureP@ss123",
"first_name": "Jane",
"last_name": "Doe",
"fingerprint": "a1b2c3d4e5f6",
"os": "Windows",
"referral_id": "ref_abc123",
"client_ip": "203.0.113.50",
"captcha": "03AGdBq24PBx..."
}'
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | User's email address. Must be unique and valid email format. |
password |
string | Yes | User's password. Minimum 8 characters. |
first_name |
string | Yes | User's first name. |
last_name |
string | Yes | User's last name. |
fingerprint |
string | No | Browser/device fingerprint for fraud detection. |
os |
string | No | Operating system identifier (e.g., "Windows", "macOS", "Linux", "iOS", "Android"). |
referral_id |
string | No | Referral tracking identifier. If provided, links the new account to the referring user. |
client_ip |
string | No | Client's IP address for geolocation and fraud detection. |
captcha |
string | No | CAPTCHA verification token (e.g., reCAPTCHA response). |
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA5MjM0NTY3LCJ1c2VyX2lkIjo0Mn0.abc123",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwOTgzOTM2NywidXNlcl9pZCI6NDJ9.xyz789"
}
| Field | Type | Description |
|---|---|---|
access |
string | JWT access token for authenticating API requests |
refresh |
string | JWT refresh token for obtaining new access tokens |
| Status | Condition | Example Response |
|---|---|---|
| 400 | Missing required fields | {"email": ["This field is required."]} |
| 400 | Invalid email format | {"email": ["Enter a valid email address."]} |
| 400 | Password too short | {"password": ["This field must be at least 8 characters."]} |
| 400 | Email already registered | {"email": ["A user with this email already exists."]} |
| 400 | CAPTCHA validation failed | {"captcha": ["CAPTCHA verification failed."]} |
| 429 | Rate limit exceeded | {"detail": "Request was throttled."} |
Authenticate with email and password to receive JWT tokens.
Authentication: None required
curl -X POST "${BASE_URL}/api/user/login/" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "SecureP@ss123"
}'
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Registered email address |
password |
string | Yes | Account password |
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA5MjM0NTY3LCJ1c2VyX2lkIjo0Mn0.abc123",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwOTgzOTM2NywidXNlcl9pZCI6NDJ9.xyz789"
}
| Status | Condition | Example Response |
|---|---|---|
| 400 | Missing fields | {"email": ["This field is required."]} |
| 401 | Invalid credentials | {"detail": "No active account found with the given credentials"} |
| 401 | Account disabled | {"detail": "No active account found with the given credentials"} |
| 429 | Rate limit exceeded | {"detail": "Request was throttled."} |
Security Note: The API returns the same error message for invalid email and invalid password to prevent email enumeration attacks.
Authenticate using a Google OAuth token. If the user does not exist, an account is automatically created.
Authentication: None required
curl -X POST "${BASE_URL}/api/user/login/google/" \
-H "Content-Type: application/json" \
-d '{
"access_token": "ya29.a0ARrdaM...",
"fingerprint": "a1b2c3d4e5f6",
"os": "macOS",
"referral_id": "ref_abc123",
"client_ip": "203.0.113.50"
}'
| Field | Type | Required | Description |
|---|---|---|---|
access_token |
string | Yes | Google OAuth access token obtained from the Google Sign-In flow on the client side |
fingerprint |
string | No | Browser/device fingerprint |
os |
string | No | Operating system identifier |
referral_id |
string | No | Referral tracking identifier |
client_ip |
string | No | Client IP address |
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
access_tokenaccess_token to this endpoint| Status | Condition | Example Response |
|---|---|---|
| 400 | Invalid or expired Google token | {"detail": "Invalid Google token."} |
| 400 | Missing access_token | {"access_token": ["This field is required."]} |
| 500 | Google API unreachable | {"detail": "Authentication service unavailable."} |
Authenticate using a Facebook OAuth token. If the user does not exist, an account is automatically created.
Authentication: None required
curl -X POST "${BASE_URL}/api/user/login/facebook/" \
-H "Content-Type: application/json" \
-d '{
"access_token": "EAAGm0PX4ZCps...",
"fingerprint": "a1b2c3d4e5f6",
"os": "iOS",
"referral_id": "ref_abc123",
"client_ip": "203.0.113.50"
}'
| Field | Type | Required | Description |
|---|---|---|---|
access_token |
string | Yes | Facebook OAuth access token obtained from the Facebook Login flow |
fingerprint |
string | No | Browser/device fingerprint |
os |
string | No | Operating system identifier |
referral_id |
string | No | Referral tracking identifier |
client_ip |
string | No | Client IP address |
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
access_tokenaccess_token to this endpoint| Status | Condition | Example Response |
|---|---|---|
| 400 | Invalid or expired Facebook token | {"detail": "Invalid Facebook token."} |
| 400 | Missing access_token | {"access_token": ["This field is required."]} |
| 500 | Facebook API unreachable | {"detail": "Authentication service unavailable."} |
Refresh an expired access token using a valid refresh token.
Authentication: None required (the refresh token itself serves as proof of authentication)
curl -X POST "${BASE_URL}/api/user/refresh/" \
-H "Content-Type: application/json" \
-d '{
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwOTgzOTM2NywidXNlcl9pZCI6NDJ9.xyz789"
}'
| Field | Type | Required | Description |
|---|---|---|---|
refresh |
string | Yes | A valid, non-expired JWT refresh token |
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA5MjM4MTY3LCJ1c2VyX2lkIjo0Mn0.newtoken123"
}
| Field | Type | Description |
|---|---|---|
access |
string | New JWT access token |
Note: The refresh token itself is not rotated on each refresh call. The same refresh token remains valid until it expires.
| Status | Condition | Example Response |
|---|---|---|
| 400 | Missing refresh token | {"refresh": ["This field is required."]} |
| 401 | Expired refresh token | {"detail": "Token is invalid or expired", "code": "token_not_valid"} |
| 401 | Invalid/malformed token | {"detail": "Token is invalid or expired", "code": "token_not_valid"} |
| 401 | Blacklisted token | {"detail": "Token is blacklisted", "code": "token_not_valid"} |
The recommended approach for handling token expiration in the frontend:
1. Intercept 401 responses from API calls
2. Check if the error is due to an expired access token
3. Call POST /api/user/refresh/ with the stored refresh token
4. If refresh succeeds:
- Store the new access token
- Retry the original request with the new token
5. If refresh fails (401):
- Clear all stored tokens
- Redirect user to login page
Retrieve the authenticated user's profile information.
Authentication: Required (Bearer token)
curl -X GET "${BASE_URL}/api/user/account/" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json"
{
"id": 42,
"email": "user@example.com",
"first_name": "Jane",
"last_name": "Doe",
"plan": "pro",
"credits_remaining": 48500,
"requests_remaining": 1150,
"credits_total": 55000,
"requests_total": 1200,
"subscription_status": "active",
"subscription_end_date": "2026-02-28T00:00:00Z",
"created_at": "2025-06-15T10:30:00Z",
"referral_code": "ref_jane42",
"is_verified": true
}
| Field | Type | Description |
|---|---|---|
id |
integer | Unique user identifier |
email |
string | User's email address |
first_name |
string | User's first name |
last_name |
string | User's last name |
plan |
string | Current subscription plan (free, starter, pro, unlimited, trial, special) |
credits_remaining |
integer | Number of credits remaining in the current billing cycle |
requests_remaining |
integer | Number of API requests remaining in the current billing cycle |
credits_total |
integer | Total credits allocated for the current plan |
requests_total |
integer | Total requests allocated for the current plan |
subscription_status |
string | Subscription status (active, canceled, past_due, trialing) |
subscription_end_date |
string | ISO 8601 datetime when the current subscription period ends |
created_at |
string | ISO 8601 datetime when the account was created |
referral_code |
string | User's unique referral code for sharing |
is_verified |
boolean | Whether the user's email has been verified |
| Status | Condition | Example Response |
|---|---|---|
| 401 | Missing or invalid token | {"detail": "Authentication credentials were not provided."} |
| 401 | Expired access token | {"detail": "Given token not valid for any token type", "code": "token_not_valid"} |
Request a password reset email. Sends a reset link to the provided email address if an account exists.
Authentication: None required
curl -X POST "${BASE_URL}/api/user/forgot-password/" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com"
}'
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Email address associated with the account |
{
"detail": "Password reset email has been sent."
}
Security Note: The API returns a success response regardless of whether the email exists in the system. This prevents email enumeration attacks.
| Status | Condition | Example Response |
|---|---|---|
| 400 | Missing email field | {"email": ["This field is required."]} |
| 400 | Invalid email format | {"email": ["Enter a valid email address."]} |
| 429 | Rate limit exceeded | {"detail": "Request was throttled."} |
Reset the user's password using a token received via the password reset email.
Authentication: None required (the reset token serves as authentication)
curl -X POST "${BASE_URL}/api/user/reset-password/" \
-H "Content-Type: application/json" \
-d '{
"token": "abc123def456ghi789",
"password": "NewSecureP@ss456"
}'
| Field | Type | Required | Description |
|---|---|---|---|
token |
string | Yes | Password reset token from the email link |
password |
string | Yes | New password. Must meet the minimum password requirements (at least 8 characters). |
{
"detail": "Password has been reset successfully."
}
| Status | Condition | Example Response |
|---|---|---|
| 400 | Invalid or expired token | {"detail": "Invalid or expired reset token."} |
| 400 | Password too short | {"password": ["This field must be at least 8 characters."]} |
| 400 | Missing fields | {"token": ["This field is required."]} |
1. User clicks "Forgot Password" on frontend
2. Frontend calls POST /api/user/forgot-password/ with email
3. Backend sends reset email with a unique token (embedded in URL)
4. User clicks link in email, lands on reset password page
5. Frontend extracts token from URL
6. User enters new password
7. Frontend calls POST /api/user/reset-password/ with token + new password
8. On success, redirect user to login page
The JWT tokens issued by the API contain the following claims:
{
"token_type": "access",
"exp": 1709234567,
"iat": 1709230967,
"jti": "unique-token-id",
"user_id": 42
}
| Claim | Description |
|---|---|
token_type |
Always "access" for access tokens |
exp |
Expiration timestamp (Unix epoch) |
iat |
Issued-at timestamp (Unix epoch) |
jti |
Unique token identifier |
user_id |
The authenticated user's ID |
{
"token_type": "refresh",
"exp": 1709839367,
"iat": 1709230967,
"jti": "unique-token-id",
"user_id": 42
}
Note: Never decode JWT tokens on the client to make authorization decisions. Always rely on the server to validate tokens and return appropriate data.
| Date | Author | Change |
|---|---|---|
| 2026-01-30 | Admin | Initial creation |
Prev: API Reference - Overview | Next: API Reference - Humanizer Endpoints | Up: WalterWrites