Complete reference for all Neev API endpoints. All API routes are prefixed with the configurable route prefix — route_prefix in config/neev.php (env NEEV_ROUTE_PREFIX), default neev. This documentation uses the default /neev prefix throughout.
All authenticated endpoints require a Bearer token in the Authorization header:
Authorization: Bearer {token_id}|{token}The header is the only accepted transport — query-string and request-body tokens were removed in v0.4.4 (tokens in URLs leak via logs, referrers, and browser history).
SPA cookie mode: same-origin SPAs whose host is listed in config('neev.spa.stateful') may instead carry the token in an HttpOnly cookie — the EnsureSpaRequestsAreStateful middleware promotes it to the Authorization header. State-changing requests (POST/PUT/PATCH/DELETE) from stateful origins must echo the CSRF cookie in the X-XSRF-TOKEN header or they are rejected with 419. See SPA Cookie Mode.
Issues the signed double-submit CSRF cookie. SPAs call this once on app load (and again after a 419).
GET /neev/csrf-cookieResponse: 204 No Content with an XSRF-TOKEN cookie (not HttpOnly — the SPA reads it and echoes the value in X-XSRF-TOKEN). Throttled to 60 requests/minute.
Create a new user account.
POST /neev/registerRequest Body:
{
"name": "John Doe",
"email": "john@example.com",
"password": "SecurePass123!",
"password_confirmation": "SecurePass123!",
"username": "johndoe" // Optional, if support_username enabled
}Response:
{
"auth_state": "authenticated",
"token": "1|abc123...",
"expires_in": 1440,
"mfa_options": null,
"email_verified": false
}Authenticate with email/username and password.
POST /neev/loginRequest Body:
{
"email": "john@example.com",
"password": "SecurePass123!"
}Response (without MFA):
{
"auth_state": "authenticated",
"token": "1|abc123...",
"expires_in": 1440,
"mfa_options": null,
"email_verified": true
}Response (with MFA):
{
"auth_state": "mfa_required",
"token": "jwt_mfa_token...",
"expires_in": 30,
"mfa_options": [
"authenticator",
"email"
],
"email_verified": true
}When auth_state is mfa_required, the token is a short-lived JWT (type mfa) that can only be used to verify MFA. Complete MFA verification to get a full access token.
expires_in is returned in minutes.
Send a magic link to the user's email for passwordless login.
POST /neev/sendLoginLinkRequest Body:
{
"email": "john@example.com"
}Response:
{
"message": "Login link has been sent."
}Authenticate using a magic link.
GET /neev/loginUsingLink?id={email_id}&signature={signature}&expires={timestamp}Response:
{
"auth_state": "authenticated",
"token": "1|abc123...",
"expires_in": 1440,
"email_verified": true
}Logout the current session.
POST /neev/logoutHeaders:
Authorization: Bearer {token}Response:
{
"message": "Logged out successfully."
}Logout from all other devices — the current session survives.
POST /neev/logoutAllHeaders:
Authorization: Bearer {token}Response:
{
"message": "Logged out from all other devices successfully."
}Send a signed URL password reset link to the user's email.
POST /neev/forgotPasswordRequest Body:
{
"email": "john@example.com"
}Response:
{
"message": "Password reset link has been sent to your email."
}Reset the user's password using a signed URL from the forgot password email. The frontend receives the signed URL parameters and forwards them to this endpoint.
POST /neev/resetPassword?id={user_id}&hash={email_hash}&signature={signature}&expires={timestamp}Request Body:
{
"password": "NewSecurePass123!",
"password_confirmation": "NewSecurePass123!"
}Response:
{
"message": "Password has been updated."
}Resend the verification email to the authenticated user's current email address.
POST /neev/email/sendHeaders:
Authorization: Bearer {token}Response:
{
"message": "Verification link has been sent."
}GET /neev/email/verify?id={user_id}&hash={email_hash}&signature={signature}&expires={timestamp}Headers:
Authorization: Bearer {token}Response:
{
"message": "Email verification done."
}Request to change the authenticated user's email address. Sends a verification link to the new email. Requires current password for security.
POST /neev/email/changeHeaders:
Authorization: Bearer {token}Request Body:
{
"email": "newemail@example.com",
"password": "CurrentPass123!"
}Response:
{
"message": "Verification link has been sent to your new email address."
}Verify the email change using the signed URL sent to the new email address. The frontend receives the signed URL parameters and forwards them to this endpoint.
POST /neev/email/change/verify?id={user_id}&email={new_email}&signature={signature}&expires={timestamp}Response (success):
{
"message": "Email address has been updated and verified."
}Response (email already taken):
{
"message": "This email address is already in use."
}POST /neev/mfa/addHeaders:
Authorization: Bearer {token}Request Body:
{
"auth_method": "authenticator" // or "email"
}Response (authenticator):
{
"qr_code": "<svg>...</svg>",
"secret": "JBSWY3DPEHPK3PXP",
"method": "authenticator"
}The authenticator method is created in a pending state and is not enforced at login until activated via Verify MFA Setup. The email method is created active immediately.
Response (email):
{
"message": "Email Configured."
}Activate a pending authenticator setup by verifying a TOTP code. On success the method becomes active (and preferred, if no other active method is preferred).
POST /neev/mfa/setup/verifyHeaders:
Authorization: Bearer {token}Request Body:
{
"auth_method": "authenticator",
"otp": "123456"
}Response:
{
"message": "Method has been verified and enabled.",
"method": "authenticator"
}Response (wrong code or no pending setup, 400):
{
"message": "Code verification failed."
}Complete MFA verification after login.
POST /neev/mfa/otp/verifyHeaders:
Authorization: Bearer {mfa_jwt_token}Request Body:
{
"auth_method": "authenticator",
"otp": "123456"
}Response:
{
"auth_state": "authenticated",
"token": "1|abc123...",
"expires_in": 1440,
"email_verified": true
}DELETE /neev/mfa/deleteHeaders:
Authorization: Bearer {token}Request Body:
{
"auth_method": "authenticator"
}Response:
{
"message": "Auth has been deleted."
}POST /neev/recoveryCodesHeaders:
Authorization: Bearer {token}Response:
{
"message": "New recovery codes are generated.",
"data": [
"abc123defg",
"hij456klmn",
"opq789rstu"
]
}GET /neev/passkeys/register/optionsHeaders:
Authorization: Bearer {token}Response:
{
"rp": {
"name": "Your App",
"id": "yourapp.com"
},
"user": {
"id": "base64_user_id",
"name": "john@example.com",
"displayName": "John Doe"
},
"challenge": "base64_challenge",
"pubKeyCredParams": [...],
"authenticatorSelection": {
"residentKey": "required",
"userVerification": "required"
},
"timeout": 60000,
"attestation": "none"
}POST /neev/passkeys/registerHeaders:
Authorization: Bearer {token}Request Body:
{
"attestation": "{...attestation_response_json...}",
"name": "My MacBook"
}Response:
{
"message": "Passkey has been registered.",
"data": {
"id": 1,
"name": "My MacBook",
"created_at": "2024-01-15T10:00:00Z"
}
}GET /neev/passkeys/login/options?email=john@example.comResponse:
{
"challenge": "base64_challenge",
"timeout": 120000,
"rpId": "yourapp.com",
"allowCredentials": [...],
"userVerification": "required"
}POST /neev/passkeys/loginRequest Body:
{
"email": "john@example.com",
"assertion": "{...assertion_response_json...}"
}Response:
{
"auth_state": "authenticated",
"token": "1|abc123...",
"expires_in": 1440,
"email_verified": true
}PUT /neev/passkeysHeaders:
Authorization: Bearer {token}Request Body:
{
"passkey_id": 1,
"name": "Work Laptop"
}DELETE /neev/passkeysHeaders:
Authorization: Bearer {token}Request Body:
{
"passkey_id": 1
}GET /neev/usersHeaders:
Authorization: Bearer {token}Response:
{
"data": {
"id": 1,
"name": "John Doe",
"username": "johndoe",
"active": true,
"emails": [...],
"teams": [...]
}
}PUT /neev/usersHeaders:
Authorization: Bearer {token}Request Body:
{
"name": "John Smith",
"username": "johnsmith"
}Response:
{
"message": "Account has been updated.",
"data": {...}
}DELETE /neev/usersHeaders:
Authorization: Bearer {token}Request Body:
{
"password": "CurrentPassword123!"
}Response:
{
"message": "Account has been deleted."
}PUT /neev/changePasswordHeaders:
Authorization: Bearer {token}Request Body:
{
"current_password": "OldPassword123!",
"password": "NewPassword456!",
"password_confirmation": "NewPassword456!"
}Response:
{
"message": "Password has been successfully updated."
}GET /neev/sessionsHeaders:
Authorization: Bearer {token}Response:
{
"data": [
{
"id": 1,
"name": "login",
"last_used_at": "2024-01-15T10:00:00Z",
"attempt": {
"ip_address": "192.168.1.1",
"browser": "Chrome",
"platform": "macOS",
"location": "San Francisco, CA, US"
}
}
]
}DELETE /neev/sessions/{id}Deletes one of the user's login sessions (the underlying login token), immediately invalidating it. The current session cannot be revoked this way — use POST /neev/logout instead.
Headers:
Authorization: Bearer {token}Response:
{
"message": "Session has been revoked."
}Errors:
| Status | Condition |
|---|---|
| 400 | {id} is the current session |
| 404 | Session does not exist or belongs to another user |
GET /neev/loginAttemptsHeaders:
Authorization: Bearer {token}Response:
{
"data": [
{
"id": 1,
"method": "password",
"multi_factor_method": "authenticator",
"ip_address": "192.168.1.1",
"browser": "Chrome",
"platform": "macOS",
"device": "Desktop",
"location": "San Francisco, CA, US",
"is_success": true,
"is_suspicious": false,
"created_at": "2024-01-15T10:00:00Z"
}
]
}GET /neev/apiTokensHeaders:
Authorization: Bearer {token}Response:
{
"data": [
{
"id": 1,
"name": "Mobile App",
"permissions": ["read", "write"],
"last_used_at": "2024-01-15T10:00:00Z",
"expires_at": null,
"created_at": "2024-01-10T10:00:00Z"
}
]
}POST /neev/apiTokensHeaders:
Authorization: Bearer {token}Request Body:
{
"name": "Mobile App",
"permissions": ["read", "write"],
"expiry": 43200 // minutes (30 days), null for no expiry
}Response:
{
"message": "Token has been added.",
"data": {
"accessToken": {...},
"plainTextToken": "1|abc123..."
}
}PUT /neev/apiTokensHeaders:
Authorization: Bearer {token}Request Body:
{
"token_id": 1,
"name": "Updated Name",
"permissions": ["read"],
"expiry": 10080 // new expiry in minutes
}DELETE /neev/apiTokensHeaders:
Authorization: Bearer {token}Request Body:
{
"token_id": 1
}DELETE /neev/apiTokens/deleteAllHeaders:
Authorization: Bearer {token}GET /neev/teamsHeaders:
Authorization: Bearer {token}Response:
{
"data": [
{
"id": 1,
"name": "My Team",
"slug": "my-team",
"is_public": false,
"owner": {...},
"membership": {
"role": "admin",
"joined": true
}
}
]
}Set the user's default team (the team to land on after login).
PUT /neev/teams/defaultHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1
}Response:
{
"message": "Default team updated successfully.",
"data": {...}
}GET /neev/teams/{id}Headers:
Authorization: Bearer {token}Response:
{
"data": {
"id": 1,
"name": "My Team",
"slug": "my-team",
"is_public": false,
"owner": {...},
"users": [...],
"joinRequests": [...],
"invitedUsers": [...],
"invitations": [...]
}
}POST /neev/teamsHeaders:
Authorization: Bearer {token}Request Body:
{
"name": "New Team",
"public": false
}PUT /neev/teamsHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1,
"name": "Updated Team Name",
"public": true
}DELETE /neev/teamsHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1
}POST /neev/teams/inviteUserHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1,
"email": "newmember@example.com",
"role": "member"
}PUT /neev/teams/inviteUserHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1,
"action": "accept" // or "reject"
}Or for email invitations:
{
"invitation_id": 1,
"action": "accept"
}PUT /neev/teams/leaveHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1
}POST /neev/teams/requestHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1
}PUT /neev/teams/requestHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1,
"user_id": 5,
"action": "accept",
"role": "member"
}POST /neev/changeTeamOwnerHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1,
"user_id": 5
}PUT /neev/role/changeHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1,
"user_id": 5,
"role": "admin"
}GET /neev/domains?team_id=1Headers:
Authorization: Bearer {token}POST /neev/domainsHeaders:
Authorization: Bearer {token}Request Body:
{
"team_id": 1,
"domain": "company.com",
"enforce": false
}Response:
{
"message": "Domain federated successfully.",
"token": "abc123verification..."
}PUT /neev/domainsHeaders:
Authorization: Bearer {token}Request Body:
{
"domain_id": 1,
"verify": true
}DELETE /neev/domainsHeaders:
Authorization: Bearer {token}Request Body:
{
"domain_id": 1
}GET /neev/tenant-domainsHeaders:
Authorization: Bearer {token}POST /neev/tenant-domainsHeaders:
Authorization: Bearer {token}Request Body:
{
"domain": "custom.example.com"
}POST /neev/tenant-domains/{id}/verifyPOST /neev/tenant-domains/{id}/primaryAll endpoints return consistent error responses:
{
"message": "Error description here."
}Common HTTP Status Codes:
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request (validation error) |
| 401 | Unauthorized (invalid/missing token) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not Found |
| 500 | Server Error |