Two auth methods, token lifecycle, MFA setup, and security challenges.
Authentication Guide
UrbanPayX offers two authentication methods: email/password login for interactive use, and API key authentication for server-to-server integrations. This guide covers both methods, token management, MFA, and security challenges.
Two ways to authenticate
| API key auth | Email/password login | |
|---|---|---|
| Endpoint | POST /auth/api-token | POST /auth/login |
| Best for | Server-to-server integrations, automated workflows | Interactive dashboard use, admin tasks |
| Role | Always operations | Role of the logged-in team member |
| Access token TTL | 2 hours | 30 minutes |
| Refresh token | Not issued | Issued (30-day TTL) |
| MFA | Not applicable | Supported (email OTP or TOTP) |
Choose API key auth for your backend integration — it is simpler, has longer-lived tokens, and does not require MFA handling. Use email/password login for dashboard operations or when you need OWNER or DEVELOPER permissions that API keys do not carry.
API key authentication
The simplest way to authenticate. Send your API key and secret, get an access token.
POST /api/v1/auth/api-token
Content-Type: application/json
{
"api_key": "your-api-key",
"api_secret": "your-api-secret"
}Response:
{
"access_token": "eyJhbGciOi...",
"token_type": "bearer"
}The access token is valid for 2 hours. No refresh token is issued — when it expires, authenticate again with your API key.
Important: API key tokens always carry the operations role, regardless of who created the key. This means they can create payments and manage KYC, but cannot manage team members (OWNER only) or configure webhooks (DEVELOPER or OWNER only). See Roles and Permissions for the full matrix.
Rate limiting
After 5 failed authentication attempts, the API key is locked out for 5 minutes. The API returns 429 Too Many Requests with the lockout duration.
Email/password login
For interactive use and operations that require OWNER or DEVELOPER roles.
POST /api/v1/auth/login
Content-Type: application/json
{
"client_email": "[email protected]",
"password": "your-password"
}Without MFA
If MFA is not enabled, you get tokens immediately:
{
"access_token": "eyJhbGciOi...",
"refresh_token": "eyJhbGciOi...",
"token_type": "bearer"
}With MFA enabled
If MFA is enabled on the account (or enforced by client defaults), the login response includes a challenge:
{
"token_type": "otp_pending",
"challenge_token": "base64-encoded-token",
"requires_mfa": true,
"allowed_mfa_methods": ["email", "totp"],
"preferred_mfa_method": "email"
}Complete the challenge by verifying the OTP:
POST /api/v1/auth/login/verify-otp
Content-Type: application/json
{
"challenge_token": "base64-encoded-token",
"otp_code": "123456",
"method": "email"
}The challenge token expires after 5 minutes. You have a maximum of 5 attempts to enter the correct code. If using email OTP, there is a 60-second cooldown between resend requests.
If your preferred method is TOTP but you want to fall back to email, use:
POST /api/v1/auth/login/challenge/send-email-codeRate limiting
After 5 failed login attempts for the same email, the account is locked out for 5 minutes (429 Too Many Requests).
Token lifecycle
Access tokens
Access tokens are JWTs that authenticate every API request. Include them in the Authorization header:
Authorization: Bearer eyJhbGciOi...
| Token source | TTL | Refresh available |
|---|---|---|
| API key auth | 2 hours | No — re-authenticate |
| Email/password login | 30 minutes | Yes — use refresh token |
Refresh tokens
Dashboard login tokens come with a refresh token (30-day TTL). Use it to get a new access token without re-entering credentials:
POST /api/v1/auth/refresh
Content-Type: application/json
{
"refresh_token": "eyJhbGciOi..."
}Response:
{
"access_token": "new-access-token",
"refresh_token": "new-refresh-token",
"token_type": "bearer"
}Important: Token refresh uses rotation — each refresh returns a new refresh token and invalidates the old one. Always store and use the latest refresh token.
Token payload
Access tokens contain these claims:
| Claim | Description |
|---|---|
sub | Your client ID |
type | Token type (client_access or client_api_access) |
actor_type | client_user (login) or client_api (API key) |
role | owner, operations, or developer |
account_id | Team member account ID (login tokens only) |
exp | Expiration timestamp |
Logout
To revoke tokens and end a session:
POST /api/v1/auth/logout
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"refresh_token": "your-refresh-token"
}This revokes both the access token and the refresh session. The refresh token field is optional — if omitted, only the access token is revoked.
MFA (Multi-Factor Authentication)
MFA adds a second verification step to dashboard logins. It supports two methods: email OTP and TOTP (authenticator app).
MFA policy hierarchy
MFA can be configured at two levels:
- Client defaults — Apply to all team members unless overridden. Managed by OWNER or OPERATIONS roles via
GET/PUT /auth/mfa/client-defaults. - Account overrides — Individual team members can override the client defaults (if
mfa_use_client_defaultis set tofalse). Managed viaGET/PUT /auth/mfa/me.
Setting up TOTP
To enable authenticator app support:
Step 1 — Start enrollment:
POST /api/v1/auth/mfa/totp/enroll/start
Authorization: Bearer YOUR_ACCESS_TOKENReturns a secret key and otpauth_uri that you scan with your authenticator app (Google Authenticator, Microsoft Authenticator, etc.). The enrollment session lasts 10 minutes.
Step 2 — Confirm enrollment:
POST /api/v1/auth/mfa/totp/enroll/confirm
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"enrollment_token": "token-from-step-1",
"otp_code": "123456"
}Enter the 6-digit code from your authenticator app to confirm. TOTP codes refresh every 30 seconds, and the system accepts codes within a 1-step window.
Disabling TOTP
Disabling TOTP is a sensitive action that requires a security challenge (see below):
POST /api/v1/auth/mfa/totp/disable
Authorization: Bearer YOUR_ACCESS_TOKEN
X-Security-Token: challenge-token
X-Security-Code: 123456
X-Security-Method: emailSecurity challenges
Certain high-impact operations require an additional verification step beyond your access token. These operations include:
| Action | Description |
|---|---|
ADD_MEMBER | Creating a new team account |
UPDATE_MEMBER | Modifying a team account |
DELETE_MEMBER | Removing a team account |
CHANGE_PASSWORD | Changing your account password |
REFRESH_API_KEY | Rotating your API credentials |
DISABLE_TOTP | Disabling authenticator app MFA |
How security challenges work
Step 1 — Request a security code:
POST /api/v1/auth/security/request-code
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"action": "ADD_MEMBER",
"method": "email"
}This sends a one-time code to your email (or you can use "totp" if you have an authenticator set up). The response includes a challenge_token that expires in 5 minutes.
Step 2 — Include the security headers in your request:
POST /api/v1/auth/team/accounts
Authorization: Bearer YOUR_ACCESS_TOKEN
X-Security-Token: challenge-token-from-step-1
X-Security-Code: 123456
X-Security-Method: email
Content-Type: application/json
{
"email": "[email protected]",
"full_name": "New Member",
"role": "developer"
}You have 5 attempts to enter the correct code before the challenge is invalidated.
Client registration
New client registration follows this flow:
- Register:
POST /auth/registerwith email, password, and company details - Verify email: A verification code is sent to the registered email (valid for 24 hours)
- Confirm:
POST /auth/register/verify-emailwith the code - Login: Use the registered credentials to authenticate
If you need to resend the verification code, use POST /auth/register/resend-verification (60-second cooldown between resends).
Note: Client registration may be disabled. If you receive a 403 error, contact the UrbanPayX team to get your account provisioned.
Password management
Changing your password
Password changes require a security challenge:
POST /api/v1/auth/team/accounts/me/change-password
Authorization: Bearer YOUR_ACCESS_TOKEN
X-Security-Token: challenge-token
X-Security-Code: 123456
X-Security-Method: email
Content-Type: application/json
{
"current_password": "old-password",
"new_password": "new-password"
}The new password must be at least 8 characters and must differ from the current password.
Quick reference
| Value | Duration |
|---|---|
| API key access token TTL | 2 hours |
| Dashboard access token TTL | 30 minutes |
| Refresh token TTL | 30 days |
| OTP code TTL | 5 minutes |
| OTP max attempts | 5 |
| OTP resend cooldown | 60 seconds |
| TOTP enrollment TTL | 10 minutes |
| Security challenge TTL | 5 minutes |
| Login lockout (after 5 failures) | 5 minutes |
| Email verification TTL | 24 hours |
Related guides
- Getting Started — Your first API call
- Roles and Permissions — Who can do what with each token type
- Error Reference — All authentication errors explained