curl --request POST \
--url https://pria.praxislxp.com/api/auth/signin \
--header 'Content-Type: application/json' \
--data '
{
"email": "john.doe@mydomain.com",
"password": "iLovePria123"
}
'{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NDMwNzM2ZmQ2MmQ2NTAwNDA0MjA2NzQiLCJlbWFpbCI6ImpvaG4uZG9lQG15ZG9tYWluLmNvbSIsImN1c3RvbWVySWQiOiJjdXNfTnh4eHh4eCIsImFjY291bnRUeXBlIjoidXNlciIsInNlc3Npb25JZCI6InMlM0FhYmMxMjMiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDA4NjQwMH0.signature",
"profile": {
"_id": "<string>",
"email": "jsmith@example.com",
"fname": "<string>",
"lname": "<string>",
"picture": "<string>",
"accountType": "<string>",
"permissions": [
"<string>"
],
"customerId": "<string>",
"lxp_user_id": "<string>",
"lxp_user_type": 123,
"lxp_partner_id": "<string>",
"lxp_partner_name": "<string>",
"lxp_role_id": 123,
"lxp_role_name": "<string>",
"credits": 123,
"creditsUsed": 123,
"plan": "<string>",
"status": "<string>",
"trial_end": "2023-11-07T05:31:56Z",
"trial_used": true,
"current_period_end": "2023-11-07T05:31:56Z",
"cancel_at_period_end": true,
"referralId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"referrerPaid": true,
"resetCodeId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoices_urls": [
"<string>"
],
"remember_history_count": 123,
"browser_voice": "<string>",
"rt_voice": "<string>",
"use_location": true,
"showSideBar": true,
"dark_mode": true,
"created": "2023-11-07T05:31:56Z",
"__v": 123,
"institution": {
"_id": "<string>",
"name": "<string>",
"picture": "<string>",
"picture_bg": "<string>",
"picture_dark_bg": "<string>",
"picture_animated": "<string>",
"elevenlabs_agent_id": "<string>",
"credits": 123,
"status": "<string>",
"allowJoining": "<string>",
"joiningAdminOnly": true,
"publicId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"publicAuthorizedUrls": [
"<string>"
],
"ainame": "<string>",
"contactEmail": "jsmith@example.com",
"creditAward": 123,
"poolCredits": true,
"invoices_urls": [
"<string>"
],
"maxCompletionTokens": 123,
"disableFileUploadForUser": true,
"disableAudioNotesForUser": true,
"toolsDisabled": [
"<string>"
],
"ltiContextIds": [
"<string>"
],
"personalisationAsked": true,
"locationEnabled": true,
"rtEnabled": true,
"rtAdminOnly": true,
"displayAgentDetails": true,
"displayThinkingDetails": true,
"displayThinkingExecution": true,
"displayToolExecution": true,
"assistantsDisabled": [
"<string>"
],
"disableAssistantsForUser": true,
"rtVoice": "<string>",
"maxFiles": 123,
"questionType": "<string>",
"creditsTotal": 123,
"creditsUsagePct": 123,
"id": "<string>"
}
},
"mfaRequired": true,
"challengeId": "6856fa89cbafcff8d98680f5",
"maskedEmail": "j*****e@example.com",
"mandatorySuper": true
}User authentication and sign-in
Authenticates a user with email and password, and returns a JWT token along with the user profile.
Rate Limiting: 10 requests per minute per IP address.
JWT Token Lifecycle
On successful authentication, the response includes a token field containing a signed JWT.
Token payload:
_id— User’s unique identifieremail— User’s email addresscustomerId— Stripe customer ID (if applicable)accountType— One ofsuper,admin, orusersessionId— Server-side session identifieriat— Issued-at timestamp (set automatically by JWT)exp— Expiration timestamp (set automatically by JWT)
Token expiration: 6 hours (21,600 seconds) by default. Configurable via JWT_VALIDITY_SEC environment variable.
Using the Token
Include the JWT in every subsequent API request using one of these methods (in priority order):
-
x-access-tokenheader (recommended):x-access-token: eyJhbGciOiJIUzI1NiIs... -
Authorizationheader with Bearer scheme:Authorization: Bearer eyJhbGciOiJIUzI1NiIs... -
Query parameter:
GET /api/resource?token=eyJhbGciOiJIUzI1NiIs... -
Request body field:
{ "token": "eyJhbGciOiJIUzI1NiIs..." }
Token Errors
When a token is missing, expired, or invalid, the API returns:
- 403 — No token provided (
Authentication Required) - 401 — Token expired (
jwt expired) or token invalid (invalid signature)
Token Renewal (Sliding Session)
Tokens are automatically refreshed via a sliding session mechanism. Each time the client calls
POST /api/user/refresh/profile, the response includes a fresh JWT token with a new expiration.
This extends the session without requiring re-authentication, as long as the current token is still valid.
The frontend calls this endpoint on every page load, so active users never experience token expiration. If the token expires (e.g., user is inactive for more than 6 hours), a new sign-in is required.
curl --request POST \
--url https://pria.praxislxp.com/api/auth/signin \
--header 'Content-Type: application/json' \
--data '
{
"email": "john.doe@mydomain.com",
"password": "iLovePria123"
}
'{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NDMwNzM2ZmQ2MmQ2NTAwNDA0MjA2NzQiLCJlbWFpbCI6ImpvaG4uZG9lQG15ZG9tYWluLmNvbSIsImN1c3RvbWVySWQiOiJjdXNfTnh4eHh4eCIsImFjY291bnRUeXBlIjoidXNlciIsInNlc3Npb25JZCI6InMlM0FhYmMxMjMiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDA4NjQwMH0.signature",
"profile": {
"_id": "<string>",
"email": "jsmith@example.com",
"fname": "<string>",
"lname": "<string>",
"picture": "<string>",
"accountType": "<string>",
"permissions": [
"<string>"
],
"customerId": "<string>",
"lxp_user_id": "<string>",
"lxp_user_type": 123,
"lxp_partner_id": "<string>",
"lxp_partner_name": "<string>",
"lxp_role_id": 123,
"lxp_role_name": "<string>",
"credits": 123,
"creditsUsed": 123,
"plan": "<string>",
"status": "<string>",
"trial_end": "2023-11-07T05:31:56Z",
"trial_used": true,
"current_period_end": "2023-11-07T05:31:56Z",
"cancel_at_period_end": true,
"referralId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"referrerPaid": true,
"resetCodeId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoices_urls": [
"<string>"
],
"remember_history_count": 123,
"browser_voice": "<string>",
"rt_voice": "<string>",
"use_location": true,
"showSideBar": true,
"dark_mode": true,
"created": "2023-11-07T05:31:56Z",
"__v": 123,
"institution": {
"_id": "<string>",
"name": "<string>",
"picture": "<string>",
"picture_bg": "<string>",
"picture_dark_bg": "<string>",
"picture_animated": "<string>",
"elevenlabs_agent_id": "<string>",
"credits": 123,
"status": "<string>",
"allowJoining": "<string>",
"joiningAdminOnly": true,
"publicId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"publicAuthorizedUrls": [
"<string>"
],
"ainame": "<string>",
"contactEmail": "jsmith@example.com",
"creditAward": 123,
"poolCredits": true,
"invoices_urls": [
"<string>"
],
"maxCompletionTokens": 123,
"disableFileUploadForUser": true,
"disableAudioNotesForUser": true,
"toolsDisabled": [
"<string>"
],
"ltiContextIds": [
"<string>"
],
"personalisationAsked": true,
"locationEnabled": true,
"rtEnabled": true,
"rtAdminOnly": true,
"displayAgentDetails": true,
"displayThinkingDetails": true,
"displayThinkingExecution": true,
"displayToolExecution": true,
"assistantsDisabled": [
"<string>"
],
"disableAssistantsForUser": true,
"rtVoice": "<string>",
"maxFiles": 123,
"questionType": "<string>",
"creditsTotal": 123,
"creditsUsagePct": 123,
"id": "<string>"
}
},
"mfaRequired": true,
"challengeId": "6856fa89cbafcff8d98680f5",
"maskedEmail": "j*****e@example.com",
"mandatorySuper": true
}Documentation Index
Fetch the complete documentation index at: https://docs.praxis-ai.com/llms.txt
Use this file to discover all available pages before exploring further.
Body
Response
Successful authentication. Returns JWT token and user profile.
Successful signin response shape. Two variants are returned by the
same endpoint depending on whether email MFA is required:
• JWT issued — { token, profile }. The user is signed in.
• MFA challenge — { mfaRequired: true, challengeId, maskedEmail, mandatorySuper? }.
The client must POST the 6-digit code to /api/auth/mfa/verify
with the challengeId; the verify endpoint then issues the JWT.
Discriminate via mfaRequired === true (per Phase 1 design §6.1).
Signed JWT token. Present when MFA is not required or has just been verified. Include this in subsequent API requests via the x-access-token header or Authorization Bearer header. Expires after 6 hours (configurable via JWT_VALIDITY_SEC). Automatically refreshed on profile load (sliding session).
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NDMwNzM2ZmQ2MmQ2NTAwNDA0MjA2NzQiLCJlbWFpbCI6ImpvaG4uZG9lQG15ZG9tYWluLmNvbSIsImN1c3RvbWVySWQiOiJjdXNfTnh4eHh4eCIsImFjY291bnRUeXBlIjoidXNlciIsInNlc3Npb25JZCI6InMlM0FhYmMxMjMiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDA4NjQwMH0.signature"
Show child attributes
Show child attributes
When true, the response is an MFA challenge — no JWT issued. Client should redirect to the MFA verify screen with the challengeId.
true
MongoDB ObjectId of the issued mfaChallenge. Only present when mfaRequired: true. POST this to /api/auth/mfa/verify alongside the 6-digit code.
"6856fa89cbafcff8d98680f5"
Partially-masked email address the verification code was sent to (for the verify-screen "code sent to …" prompt). Only present when mfaRequired: true.
"j*****e@example.com"
Phase 2 — when true, this MFA challenge was issued under
super-mandatory enforcement (MFA_SUPER_MANDATORY=true and the
user is past the rollout date). The verify screen should
render an explanatory banner and suppress the Cancel
affordance, since the user can't dismiss the flow without
enrolling. On successful verify, the server persists
user.mfaEnabled = true so the next signin follows the
normal phase-1 trusted-device path.
Only present when mfaRequired: true AND the gate fired.
Omitted (not false) otherwise — clients should default to
false when absent.
true
Was this page helpful?