Predictu
API Reference

Auth API

The Auth API handles user registration, login, session management, and embedded iframe authentication. It supports two authentication flows: direct auth (for standalone usage) andembed auth (for casino iframe integrations where the operator manages the user session).

Base URL: https://app.predictu.com/api/auth
Content-Type: All requests and responses use application/json.
Sessions: Successful authentication returns a JWT token with a 24-hour expiry. The token should be passed in the Authorization: Bearer <token> header for all subsequent API calls.

Authentication Flows

Predictu supports two distinct authentication flows depending on how the user accesses the platform.

Direct Authentication

Used when users access Predictu directly (not through a casino embed). Users register with an email and password, then log in to receive a session token.

1
Register - User creates an account with email + password via POST /register
2
Login - User authenticates with email + password via POST /login
3
Session - JWT token returned, used for all subsequent API calls
4
Verify - Token can be verified at any time via GET /me

Embed Authentication

Used when users access Predictu through a casino operator’s iframe embed. The operator passes a signed token from their system, and Predictu either finds an existing user or creates one automatically.

1
Operator authenticates user - User logs into the casino site using the casino’s own auth system
2
Operator generates embed token - Casino server creates a signed JWT containing the player ID and operator ID
3
Iframe loads with token - The embed URL includes the operator token as a query parameter
4
Predictu initializes session - POST /embed-init verifies the operator token, finds/creates the user, and returns a Predictu session token
5
Session active - The Predictu JWT is used for all subsequent Casino API calls within the iframe

Login

POST/api/auth/login

Authenticates a user with email and password. Returns a JWT session token on success. This endpoint is rate-limited to prevent brute-force attacks.

Request Body

{
  "email": "alice@example.com",
  "password": "s3cur3P@ssw0rd"
}
FieldTypeRequiredDescription
emailstringYesUser’s email address. Case-insensitive.
passwordstringYesUser’s password. Minimum 8 characters.

Response (200 OK)

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2025-03-19T14:32:00.000Z",
  "user": {
    "id": "usr_def456",
    "email": "alice@example.com",
    "username": "alice",
    "tier": "regular",
    "operator_id": "op_abc123",
    "created_at": "2025-01-15T10:00:00.000Z"
  }
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORMissing or invalid email/password format
401INVALID_CREDENTIALSEmail not found or password incorrect
403ACCOUNT_BANNEDUser account has been banned by an admin
429RATE_LIMITEDToo many login attempts. Try again after cooldown.
Rate limiting: Login is limited to 5 attempts per 15 minutes per email address, and 20 attempts per 15 minutes per IP address. After exceeding the limit, the response includes a retry_after field (in seconds).

Security Notes

  • Passwords are hashed with bcrypt (cost factor 12) and never stored in plaintext
  • Failed login attempts are logged with IP address and user agent for security auditing
  • The error message for wrong email vs. wrong password is intentionally identical (INVALID_CREDENTIALS) to prevent email enumeration
  • Successful login clears the failed attempt counter for that email

Register

POST/api/auth/register

Creates a new user account. The user is assigned to the new tier by default and receives an initial balance of $0. If an operator_id is provided, the user is associated with that operator.

Request Body

{
  "email": "alice@example.com",
  "password": "s3cur3P@ssw0rd",
  "username": "alice",
  "operator_id": "op_abc123"
}
FieldTypeRequiredDescription
emailstringYesValid email address. Must be unique across the platform.
passwordstringYesMinimum 8 characters, must contain at least one uppercase letter, one lowercase letter, and one number.
usernamestringYesDisplay name. 3–30 characters, alphanumeric and underscores only. Must be unique.
operator_idstringNoOperator to associate the user with. If omitted, the user is a “direct” user.

Response (201 Created)

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2025-03-19T14:32:00.000Z",
  "user": {
    "id": "usr_new789",
    "email": "alice@example.com",
    "username": "alice",
    "tier": "new",
    "operator_id": "op_abc123",
    "balance": 0.00,
    "created_at": "2025-03-18T14:32:00.000Z"
  }
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORMissing or invalid fields (details in error.details)
409EMAIL_EXISTSAn account with this email already exists
409USERNAME_EXISTSThis username is already taken
404OPERATOR_NOT_FOUNDThe specified operator_id does not exist
403OPERATOR_INACTIVEThe specified operator is not active (still onboarding or disabled)

Side Effects

When a user registers successfully, the following side effects occur:

  • A row is inserted into the users table with tier new
  • A default user_scores row is created with neutral metric values
  • If operator_id is provided, the operator receives a USER_REGISTERED S2S callback
  • The registration event is logged in the audit trail
  • A session token is automatically generated (the user is logged in immediately)

Get Current User

GET/api/auth/me

Returns the profile of the currently authenticated user. This endpoint is used to verify that a session token is still valid and to fetch the latest user state (tier, balance, etc.).

Headers

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Response (200 OK)

{
  "user": {
    "id": "usr_def456",
    "email": "alice@example.com",
    "username": "alice",
    "tier": "regular",
    "classification": "moderate",
    "operator_id": "op_abc123",
    "balance": 150.00,
    "open_positions_count": 3,
    "total_trades": 42,
    "created_at": "2025-01-15T10:00:00.000Z",
    "last_trade_at": "2025-03-18T14:32:01.000Z"
  }
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDNo token provided or token is malformed
401TOKEN_EXPIREDToken has expired (24-hour lifetime exceeded)
403ACCOUNT_BANNEDUser account was banned after the token was issued
Token refresh: The /me endpoint does not refresh the token. When a token is nearing expiry, the client should call POST /login again to obtain a fresh token. Embed sessions use a different mechanism (see POST /embed-init).

Embed Init

POST/api/auth/embed-init

Initializes an authenticated session for a user within a casino operator’s embedded iframe. This is the primary authentication mechanism for the B2B casino integration. The operator signs a JWT with their secret key containing the player’s identity, and Predictu verifies it and creates/finds the corresponding Predictu user.

Request Body

{
  "operator_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcGVyYXRvcl9pZCI6Im9wX2FiYzEyMyIsInBsYXllcl9pZCI6InBsYXllcl83ODkiLCJ1c2VybmFtZSI6ImFsaWNlIiwiZXhwIjoxNzEwNzc1OTIxfQ.signature"
}
FieldTypeRequiredDescription
operator_tokenstringYesA JWT signed by the operator’s secret key containing the player identity.

Operator Token Format

The operator JWT must contain the following claims:

{
  "operator_id": "op_abc123",     // Required: the operator's ID in Predictu
  "player_id": "player_789",      // Required: the player's ID in the operator's system
  "username": "alice",             // Optional: display name (used if creating new user)
  "email": "alice@example.com",   // Optional: email (used if creating new user)
  "exp": 1710775921               // Required: expiry timestamp (max 5 minutes from now)
}
Token lifetime: Operator tokens must expire within 5 minutes of issuance. This prevents replay attacks. Tokens with an exp more than 5 minutes in the future are rejected.

Response (200 OK)

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2025-03-19T14:32:00.000Z",
  "user": {
    "id": "usr_embed_abc",
    "username": "alice",
    "tier": "new",
    "operator_id": "op_abc123",
    "external_player_id": "player_789",
    "balance": 0.00,
    "is_new": true,
    "created_at": "2025-03-18T14:32:00.000Z"
  }
}

The is_new field indicates whether this call created a new user (true) or found an existing one (false). This is useful for the operator to know when to trigger onboarding flows or welcome bonuses.

User Matching Logic

When /embed-init is called, the system uses the following logic to find or create the user:

1
Look up by composite key - Search for a user with matching operator_id + external_player_id
2
If found: Update last login timestamp, return existing user with is_new: false
3
If not found: Create a new user record with the operator association and return with is_new: true
4
Generate session: Create a Predictu JWT token (24h expiry) for the user

Error Responses

StatusCodeDescription
400INVALID_TOKENToken is malformed or cannot be decoded
401SIGNATURE_INVALIDToken signature does not match the operator’s secret key
401TOKEN_EXPIREDOperator token has expired
400MISSING_CLAIMSRequired claims (operator_id, player_id) missing from token
404OPERATOR_NOT_FOUNDOperator ID in token does not match any active operator
403OPERATOR_INACTIVEOperator exists but is not active
403ORIGIN_NOT_ALLOWEDRequest origin does not match operator’s whitelisted origins

Security Considerations

  • Signature verification: The operator token is verified against the operator’s secret key stored in Predictu’s database. HMAC-SHA256 is the required algorithm.
  • Origin check: In addition to token verification, the request’sOrigin or Referer header is checked against the operator’s whitelisted origins. This prevents a stolen operator token from being used on an unauthorized domain.
  • Idempotency: Calling /embed-init multiple times with the sameoperator_id + player_id is safe. It always returns the same user (with a fresh Predictu token).
  • No password: Embed users do not have passwords. They can only authenticate through the operator’s embed flow. They cannot use POST /login.

JWT Token Format

Predictu session tokens are standard JWTs with the following claims:

// Header:
{
  "alg": "HS256",
  "typ": "JWT"
}

// Payload:
{
  "sub": "usr_def456",           // User ID
  "email": "alice@example.com",  // User email (if available)
  "tier": "regular",             // User tier at time of token issuance
  "operator_id": "op_abc123",    // Operator (null for direct users)
  "role": "user",                // "user", "operator_admin", or "god_mode"
  "iat": 1710772321,             // Issued at
  "exp": 1710858721              // Expires at (24 hours later)
}
Token storage: Store tokens in memory or secure HTTP-only cookies. Never store JWTs inlocalStorage in production environments as they are vulnerable to XSS attacks. The embed integration uses postMessage to pass the token to the iframe, where it is held in memory only.

Rate Limits

EndpointLimitWindowScope
POST /login5 requests15 minutesPer email
POST /login20 requests15 minutesPer IP
POST /register3 requests15 minutesPer IP
GET /me60 requests1 minutePer user
POST /embed-init30 requests1 minutePer operator