Dashboard authentication
Request access, register with an approval token, sign in, manage the session, set up MFA, and choose the workspace kind.
All routes on this page live under https://app.pacspace.io/dashboard/auth.
POST https://app.pacspace.io/dashboard/auth/request-accessPublic routes need no session. Protected routes need the dashboard session cookie (-b pacspace-dashboard-cookies.txt), which signing in sets. A POST, PUT, PATCH, or DELETE that carries the cookie also sends X-Pacspace-CSRF: 1 and Origin: https://app.pacspace.io; without them the answer is 403 and nothing changes.
Each route here takes up to 10 requests a minute from one address, and request access takes 5; past that the answer is 429 RATE_LIMIT_EXCEEDED with retryAfterSeconds. Some refusals here, such as a wrong password, come back with a 2xx status, success: false, and the reason in error, so read success as well as the status.
Request access
PacSpace accounts are approved before a workspace is created. Start with a request-access call. It always gives the same answer, even if the email already has a request.
curl -X POST https://app.pacspace.io/dashboard/auth/request-access \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane@lab.example",
"organization": "Example Lab",
"buyerMode": "RECORDS_OPERATOR",
"useCase": "Record what our agents do in evaluations and in deployment, and share the record with outside evaluators."
}'name is up to 100 characters, organization up to 255, and useCase is optional, up to 1000.
Answer
{
"success": true,
"data": {},
"message": "Access request received. If approved, PacSpace will send next steps."
}Register
After approval, PacSpace sends a single-use approval token. It works only for the email it was approved for, and it expires, after 14 days unless PacSpace set another window. Use it to create the account.
curl -X POST https://app.pacspace.io/dashboard/auth/register \
-c pacspace-dashboard-cookies.txt \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane@lab.example",
"password": "Correct-Horse-42",
"organization": "Example Lab",
"buyerMode": "RECORDS_OPERATOR",
"approvalToken": "APPROVAL_TOKEN"
}'A password is 12 to 64 printable ASCII characters, with at least one uppercase letter, one lowercase letter, one number, and one special character. A registration that succeeds signs you in and sets the session cookie, and Sandbox starts setting up for the new workspace (see Environments).
buyerMode is the kind of workspace: RECORDS_OPERATOR for a records workspace. It can be changed under Settings until the first entry is committed, and not after. Without an approved token, or with one that has expired, been used, or was approved for another email, registration answers 403 with { "success": false, "error": "REGISTRATION_REQUIRES_APPROVAL", "statusCode": 403 } and no workspace is created.
Sign in
Sign in with email and password; the answer sets the httpOnly session cookie.
curl -X POST https://app.pacspace.io/dashboard/auth/login \
-c pacspace-dashboard-cookies.txt \
-H "Content-Type: application/json" \
-d '{
"email": "jane@lab.example",
"password": "Correct-Horse-42"
}'Answer
{
"success": true,
"data": {
"id": "cmw3h8d2q0001s601b4n6c2wd",
"email": "jane@lab.example",
"name": "Jane Doe",
"role": "admin",
"isSiteAdmin": false,
"isVerified": true,
"buyerMode": "RECORDS_OPERATOR",
"mfaEnabled": false,
"preferredEnvironment": "sandbox"
},
"message": "Login successful"
}If MFA is on and the request carries no mfaCode, sign-in answers:
{
"success": true,
"data": {
"requiresMfa": true,
"mfaTicket": "..."
},
"message": "MFA verification required"
}Complete an MFA sign-in
The ticket lasts five minutes. code is the 6-digit code from the authenticator app, or a recovery code.
curl -X POST https://app.pacspace.io/dashboard/auth/login/mfa-verify \
-c pacspace-dashboard-cookies.txt \
-H "Content-Type: application/json" \
-d '{
"mfaTicket": "MFA_TICKET_FROM_LOGIN",
"code": "123456"
}'You can also send the code with the password, as mfaCode on /login.
Refresh the session
Rotates the httpOnly refresh cookie and issues a new httpOnly access cookie. The request needs no body.
curl -X POST https://app.pacspace.io/dashboard/auth/refresh-token \
-b pacspace-dashboard-cookies.txt \
-c pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io"Sign out
Revokes the refresh cookie and clears both session cookies. The request needs no body.
curl -X POST https://app.pacspace.io/dashboard/auth/logout \
-b pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io"Verify email
The verification email links to a dashboard page that carries the token, and the page sends it here. The token lasts an hour.
curl -X POST https://app.pacspace.io/dashboard/auth/verify \
-H "Content-Type: application/json" \
-d '{ "token": "VERIFICATION_TOKEN" }'Send the verification email again
curl -X POST https://app.pacspace.io/dashboard/auth/resend-verification-email \
-H "Content-Type: application/json" \
-d '{ "email": "jane@lab.example" }'Password recovery
Ask for a reset link
curl -X POST https://app.pacspace.io/dashboard/auth/request-password-reset \
-H "Content-Type: application/json" \
-d '{ "email": "jane@lab.example" }'Set a new password
The reset link's token lasts an hour. The new password follows the same rule as at registration.
curl -X POST https://app.pacspace.io/dashboard/auth/reset-password \
-H "Content-Type: application/json" \
-d '{
"token": "RESET_TOKEN",
"password": "Battery-Staple-17"
}'Change the password while signed in
curl -X POST https://app.pacspace.io/dashboard/auth/change-password \
-b pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io" \
-H "Content-Type: application/json" \
-d '{
"currentPassword": "Correct-Horse-42",
"newPassword": "Battery-Staple-17"
}'Change email
Ask for the change. A link goes to the new address, and the email stays as it is until the link is confirmed.
curl -X POST https://app.pacspace.io/dashboard/auth/change-email \
-b pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io" \
-H "Content-Type: application/json" \
-d '{
"newEmail": "jane.doe@lab.example",
"currentPassword": "Correct-Horse-42"
}'The link opens a dashboard page that sends its token to confirm the change. The token lasts an hour.
curl -X POST https://app.pacspace.io/dashboard/auth/verify-email-change \
-H "Content-Type: application/json" \
-d '{ "token": "EMAIL_CHANGE_TOKEN" }'Multi-factor authentication (MFA)
1) Get the setup secret
curl -X POST https://app.pacspace.io/dashboard/auth/mfa/setup \
-b pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io"Answers the setup material once: secret, otpauthUrl for the authenticator app, recoveryCodes, and mfaEnabled: false.
2) Turn MFA on
code is the 6-digit code the authenticator app shows for the new secret.
curl -X POST https://app.pacspace.io/dashboard/auth/mfa/enable \
-b pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io" \
-H "Content-Type: application/json" \
-d '{ "code": "123456" }'3) Turn MFA off
curl -X POST https://app.pacspace.io/dashboard/auth/mfa/disable \
-b pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io" \
-H "Content-Type: application/json" \
-d '{
"currentPassword": "Correct-Horse-42",
"code": "123456"
}'Profile and preferences
Get the profile
curl https://app.pacspace.io/dashboard/auth/profile \
-b pacspace-dashboard-cookies.txtAnswers id, email, name, role, tenantId, isVerified, isSiteAdmin, mfaEnabled, preferredEnvironment, and buyerMode, with "Profile retrieved successfully".
Set the preferred environment
curl -X PATCH https://app.pacspace.io/dashboard/auth/preferences \
-b pacspace-dashboard-cookies.txt \
-H "X-Pacspace-CSRF: 1" \
-H "Origin: https://app.pacspace.io" \
-H "Content-Type: application/json" \
-d '{ "preferredEnvironment": "sandbox" }'preferredEnvironment is sandbox or production. It is the environment the dashboard opens in for this person.
Workspace kind
curl https://app.pacspace.io/dashboard/auth/buyer-mode \
-b pacspace-dashboard-cookies.txtAnswer
{
"success": true,
"data": {
"buyerMode": "RECORDS_OPERATOR",
"locked": false,
"hasCommittedWrite": false
}
}locked turns true once the workspace's first entry is committed. Until then, PATCH the same route with { "buyerMode": "RECORDS_OPERATOR" } to change it; it takes the admin or manager role and answers buyerMode and changed. Once locked, a change is refused with 409 BUYER_MODE_LOCKED: "This choice is fixed once your first record is committed."
Routes
| Endpoint | Method | Auth |
|---|---|---|
/dashboard/auth/request-access | POST | Public |
/dashboard/auth/register | POST | Public, with an approval token |
/dashboard/auth/login | POST | Public |
/dashboard/auth/login/mfa-verify | POST | Public, with an MFA ticket |
/dashboard/auth/refresh-token | POST | Refresh cookie |
/dashboard/auth/logout | POST | Refresh cookie |
/dashboard/auth/verify | POST | Public, with a token |
/dashboard/auth/resend-verification-email | POST | Public |
/dashboard/auth/request-password-reset | POST | Public |
/dashboard/auth/reset-password | POST | Public, with a token |
/dashboard/auth/change-password | POST | Session cookie |
/dashboard/auth/change-email | POST | Session cookie |
/dashboard/auth/verify-email-change | POST | Public, with a token |
/dashboard/auth/mfa/setup | POST | Session cookie |
/dashboard/auth/mfa/enable | POST | Session cookie |
/dashboard/auth/mfa/disable | POST | Session cookie |
/dashboard/auth/profile | GET | Session cookie |
/dashboard/auth/preferences | PATCH | Session cookie |
/dashboard/auth/buyer-mode | GET | Session cookie |
/dashboard/auth/buyer-mode | PATCH | Session cookie, admin or manager |