Skip to content

Authentication

The primaTime API uses JWT (JSON Web Token) authentication. All authenticated requests must include a valid access token in the Authorization header.

Authentication Flow

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   1. Login/     │────▶│  2. Request     │────▶│   3. Use API    │
│   Register      │     │  Access Token   │     │   with Token    │
└─────────────────┘     └─────────────────┘     └─────────────────┘

Step 1: Login or Register

First, authenticate using email and password:

Login

graphql
mutation Login {
  loginWithPassword(data: {
    email: "user@example.com"
    password: "your-password"
    rememberMe: true
  }) {
    success
    errors {
      value
    }
  }
}

The rememberMe parameter affects session duration:

  • true — Session lasts 30 days
  • false — Session lasts 1 day

Register

graphql
mutation Register {
  registerWithPassword(data: {
    email: "user@example.com"
    password: "secure-password-123"
    agreeWithTerms: true
    language: EN
    timeZone: EUROPE_PRAGUE
  }) {
    success
    errors {
      value
    }
  }
}

Email Verification

After registration, users must verify their email before they can request access tokens. Check for AccountNotVerifiedError in the errors array.

Step 2: Get an Account-Level Access Token

Skip to Step 4 if you know the Tenant ID

If you already have the tenant ID saved from a previous session (e.g., stored in your application), you can skip steps 2 and 3 and go directly to Step 4: Request a Tenant Access Token.

After login, first request an account-level access token (without specifying a tenant). This token allows you to query your available organizations:

graphql
mutation GetAccountToken {
  requestAccessToken(data: {}) {
    success
    accessToken
    expiration
    errors {
      value
    }
  }
}

Account vs Tenant Tokens

  • Account-level token (no tenantId) — Can query user account info and list organizations
  • Tenant-level token (with tenantId) — Can access all data within that organization

Step 3: Obtain the Tenant ID

Now, using the account-level token, query which organizations (tenants) the user has access to:

bash
curl -X POST https://api.next.primatime.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCOUNT_TOKEN" \
  -d '{"query": "{ authenticationContext { accesses(first: 50) { edges { node { organization { id profile { title } } } } } } }"}'

Full query:

graphql
query GetMyOrganizations {
  authenticationContext {
    accesses(first: 50) {
      edges {
        node {
          id
          organization {
            id                    # ← This is the Tenant ID
            profile {
              title
              urlPrefix
            }
          }
        }
      }
      pageInfo {
        totalCount
      }
    }
  }
}

Response:

json
{
  "data": {
    "authenticationContext": {
      "accesses": {
        "edges": [
          {
            "node": {
              "id": "access_abc123",
              "organization": {
                "id": "org_xyz789",
                "profile": {
                  "title": "Acme Corporation",
                  "urlPrefix": "acme"
                }
              }
            }
          }
        ],
        "pageInfo": {
          "totalCount": 1
        }
      }
    }
  }
}

The organization.id field (e.g., "org_xyz789") is the Tenant ID you'll use in subsequent requests.

New Users

Users who just registered won't have any organizations yet. They need to create one first using createOrganization, or accept an invitation to join an existing organization.

Step 4: Request a Tenant Access Token

Now request an access token for the specific tenant you want to work with:

graphql
mutation RequestToken {
  requestAccessToken(data: {
    tenantId: "org_xyz789"
  }) {
    success
    accessToken
    expiration
    errors {
      value
    }
  }
}

Step 5: Use the Access Token

Include the token in all subsequent requests:

bash
curl -X POST https://api.next.primatime.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..." \
  -H "X-Tenant-ID: org_xyz789" \
  -d '{"query": "{ authenticationContext { account { profile { email } } } }"}'

Cookies and Session Management

The API uses HTTP-only cookies for secure session management.

When you successfully log in, the server sets an HTTP-only cookie containing a refresh token:

PropertyValue
NamerefreshToken
HttpOnlyYes
SecureYes
Max-Age604,800 seconds (7 days)
Path/

Browser Clients

When using fetch or other HTTP clients in the browser, you must include credentials to send/receive cookies:

javascript
fetch(API_URL, {
  method: 'POST',
  credentials: 'include',  // ← Required for cookies
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ query: '...' })
});
  1. Login/Register — Server sets the refreshToken cookie
  2. Request Access Token — Server reads the cookie and issues a new access token + new refresh token cookie
  3. Logout — Server invalidates the session; client should clear the cookie

Non-Browser Clients

For server-to-server or mobile applications, you need to:

  1. Extract the Set-Cookie header from login/token responses
  2. Store the refresh token value securely
  3. Send it back in the Cookie header for subsequent token requests

Token Lifecycle and Refresh

Token Expiration Times

Token TypeValidityPurpose
Access Token12 hours (720 minutes)Short-lived token for API requests
Refresh Token7 daysUsed to obtain new access tokens
Session1–30 daysDepends on rememberMe setting

Refreshing Access Tokens

Access tokens expire after 12 hours. Before expiration, request a new access token using the refresh token cookie:

graphql
mutation RefreshAccessToken {
  requestAccessToken(data: {
    tenantId: "org_xyz789"
  }) {
    success
    accessToken
    expiration
    errors {
      value
    }
  }
}

What happens during refresh:

  1. Server validates the refresh token from the cookie
  2. Server invalidates the old refresh token (one-time use)
  3. Server issues a new access token
  4. Server sets a new refresh token cookie
  5. Response includes the new access token and expiration

Automatic Token Rotation

Each time you call requestAccessToken, the refresh token is rotated. This improves security by ensuring each refresh token can only be used once.

Required Headers

HeaderRequiredDescription
Content-TypeYesMust be application/json
AuthorizationFor authenticated endpointsBearer <access_token>
X-Tenant-IDFor tenant operationsThe organization/tenant ID

Public Operations

The following operations do not require authentication:

OperationPurpose
loginWithPasswordEmail/password login
registerWithPasswordNew user registration
requestRecoveryRequest password recovery
recoverPasswordComplete password recovery
recoveryDetailsGet recovery status
verifyEmailVerify email address
resendEmailVerificationResend verification email
inviteDetailsGet invitation details
requestAccessTokenGet access token (requires valid refresh token cookie)

All other operations require a valid access token.

Session Management

View and manage active sessions:

graphql
# List all sessions
query MySessions {
  sessions(first: 10) {
    edges {
      node {
        id
      }
    }
  }
}

# Invalidate current session (logout)
mutation Logout {
  invalidateCurrentSession {
    success
  }
}

# Invalidate all sessions (logout everywhere)
mutation LogoutAll {
  invalidateAllSessions {
    success
  }
}

# Invalidate specific session
mutation InvalidateSession {
  invalidateSession(id: "session_xyz") {
    success
  }
}

Authentication Context

After authentication, retrieve the current user's context:

graphql
query WhoAmI {
  authenticationContext {
    account {
      id
      profile {
        title
        firstName
        lastName
        email
      }
    }
    user {
      id
      access
      owner
    }
    organization {
      id
      profile {
        title
      }
    }
    permissions
  }
}

Error Responses

Authentication failures return errors in the response:

json
{
  "data": {
    "loginWithPassword": {
      "success": false,
      "errors": [
        {
          "value": "Invalid email or password"
        }
      ]
    }
  }
}

Common Authentication Errors

ErrorCauseSolution
Invalid access tokenToken expired or malformedRefresh the token
Invalid refresh tokenRefresh token expired or reusedRe-authenticate (login)
Invalid sessionSession invalidated or expiredRe-authenticate (login)
AccountNotVerifiedErrorEmail not verifiedVerify email first

If a request requires authentication but no valid token is provided:

json
{
  "errors": [
    {
      "message": "Invalid access token",
      "extensions": {
        "classification": "UNAUTHORIZED"
      }
    }
  ]
}

primaTime API Documentation