Skip to content
Last updated

This guide walks through the complete authentication flow for Freddy API, from initial login to making authenticated API requests.

Overview

Freddy uses a two-step authentication process:

  1. Login - Validate credentials and receive email verification code
  2. Verify - Confirm email code and receive JWT tokens
  3. Authenticate - Use Bearer token for API requests
  4. Refresh - Renew expired tokens without re-login

Step 1: Login with Credentials

Send user credentials to initiate the login process.

Endpoint: POST /v1/auth/login

import requests

response = requests.post(
    "https://api.aitronos.com/v1/auth/login",
    json={
        "email_or_username": "user@example.com",
        "password": "your_secure_password"
    }
)

data = response.json()
email_key = data["email_key"]  # Save this for step 2
const response = await fetch("https://api.aitronos.com/v1/auth/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email_or_username: "user@example.com",
    password: "your_secure_password"
  })
});

const data = await response.json();
const emailKey = data.email_key;  // Save this for step 2

Response:

{
  "success": true,
  "message": "Login credentials verified. Please check your email for verification code.",
  "email_key": "uuid-12345678-1234-1234-1234-123456789abc",
  "email": "user@example.com",
  "requires_verification": true,
  "next_step": "email_verification"
}

The user receives a 4-digit verification code via email (expires in 5 minutes).

Step 2: Verify Email Code

Submit the verification code received via email.

Endpoint: POST /v1/auth/verify

verification_code = 2290  # Code from email

response = requests.post(
    "https://api.aitronos.com/v1/auth/verify",
    json={
        "email_key": email_key,
        "verification_code": verification_code
    }
)

data = response.json()
access_token = data["token"]
refresh_token = data["refreshToken"]

# Store tokens securely
const verificationCode = 2290;  // Code from email

const response = await fetch("https://api.aitronos.com/v1/auth/verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email_key: emailKey,
    verification_code: verificationCode
  })
});

const data = await response.json();
const accessToken = data.token;
const refreshToken = data.refreshToken;

// Store tokens securely

Response:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 86400,
  "user": {
    "id": "usr_1e60b7a014eb4f0c9f335ce11ebc0816",
    "email": "user@example.com",
    "verified": true
  }
}

Step 3: Make Authenticated Requests

Use the Bearer token in the Authorization header for all API requests.

headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

# Create a response
response = requests.post(
    "https://api.aitronos.com/v1/responses",
    headers=headers,
    json={
        "model": "gpt-4",
        "messages": [{"role": "user", "content": "Hello!"}]
    }
)
const headers = {
  "Authorization": `Bearer ${accessToken}`,
  "Content-Type": "application/json"
};

// Create a response
const response = await fetch("https://api.aitronos.com/v1/responses", {
  method: "POST",
  headers: headers,
  body: JSON.stringify({
    model: "gpt-4",
    messages: [{ role: "user", content: "Hello!" }]
  })
});
curl -X POST "https://api.aitronos.com/v1/responses" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

Step 4: Refresh Expired Tokens

When the access token expires (after 24 hours), use the refresh token to get a new one.

Endpoint: POST /v1/auth/refresh

response = requests.post(
    "https://api.aitronos.com/v1/auth/refresh",
    json={"refresh_token": refresh_token}
)

data = response.json()
access_token = data["token"]  # New access token
const response = await fetch("https://api.aitronos.com/v1/auth/refresh", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    refresh_token: refreshToken
  })
});

const data = await response.json();
const newAccessToken = data.token;

Response:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "device_id": "device-123"
}

Complete Implementation Example

Here's a complete Python class that handles the entire flow:

import requests
from typing import Optional

class FreddyAuth:
    def __init__(self):
        self.base_url = "https://api.aitronos.com/v1"
        self.access_token: Optional[str] = None
        self.refresh_token: Optional[str] = None
    
    def login(self, email_or_username: str, password: str) -> str:
        """Step 1: Login and get email_key"""
        response = requests.post(
            f"{self.base_url}/auth/login",
            json={
                "email_or_username": email_or_username,
                "password": password
            }
        )
        response.raise_for_status()
        data = response.json()
        return data["email_key"]
    
    def verify(self, email_key: str, verification_code: int):
        """Step 2: Verify email code and store tokens"""
        response = requests.post(
            f"{self.base_url}/auth/verify",
            json={
                "email_key": email_key,
                "verification_code": verification_code
            }
        )
        response.raise_for_status()
        data = response.json()
        
        self.access_token = data["token"]
        self.refresh_token = data["refreshToken"]
        return data
    
    def refresh(self):
        """Step 4: Refresh access token"""
        if not self.refresh_token:
            raise ValueError("No refresh token available")
        
        response = requests.post(
            f"{self.base_url}/auth/refresh",
            json={"refresh_token": self.refresh_token}
        )
        response.raise_for_status()
        data = response.json()
        
        self.access_token = data["token"]
        return data
    
    def get_headers(self) -> dict:
        """Get headers for authenticated requests"""
        if not self.access_token:
            raise ValueError("Not authenticated. Call login() and verify() first.")
        
        return {
            "Authorization": f"Bearer {self.access_token}",
            "Content-Type": "application/json"
        }
    
    def create_response(self, model: str, messages: list):
        """Step 3: Make authenticated API request"""
        response = requests.post(
            f"{self.base_url}/responses",
            headers=self.get_headers(),
            json={"model": model, "messages": messages}
        )
        
        # Auto-refresh on 401
        if response.status_code == 401:
            self.refresh()
            response = requests.post(
                f"{self.base_url}/responses",
                headers=self.get_headers(),
                json={"model": model, "messages": messages}
            )
        
        response.raise_for_status()
        return response.json()

# Usage
auth = FreddyAuth()

# Step 1: Login
email_key = auth.login("user@example.com", "password123")
print(f"Check your email for verification code")

# Step 2: Verify (user enters code from email)
verification_code = int(input("Enter verification code: "))
auth.verify(email_key, verification_code)

# Step 3: Make API calls
result = auth.create_response(
    model="gpt-4",
    messages=[{"role": "user", "content": "Hello!"}]
)
print(result)

JavaScript/TypeScript Implementation

class FreddyAuth {
  constructor() {
    this.baseUrl = "https://api.aitronos.com/v1";
    this.accessToken = null;
    this.refreshToken = null;
  }

  async login(emailOrUsername, password) {
    const response = await fetch(`${this.baseUrl}/auth/login`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email_or_username: emailOrUsername,
        password: password
      })
    });

    if (!response.ok) throw new Error("Login failed");
    const data = await response.json();
    return data.email_key;
  }

  async verify(emailKey, verificationCode) {
    const response = await fetch(`${this.baseUrl}/auth/verify`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email_key: emailKey,
        verification_code: verificationCode
      })
    });

    if (!response.ok) throw new Error("Verification failed");
    const data = await response.json();
    
    this.accessToken = data.token;
    this.refreshToken = data.refreshToken;
    return data;
  }

  async refresh() {
    if (!this.refreshToken) {
      throw new Error("No refresh token available");
    }

    const response = await fetch(`${this.baseUrl}/auth/refresh`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ refresh_token: this.refreshToken })
    });

    if (!response.ok) throw new Error("Token refresh failed");
    const data = await response.json();
    
    this.accessToken = data.token;
    return data;
  }

  getHeaders() {
    if (!this.accessToken) {
      throw new Error("Not authenticated");
    }
    return {
      "Authorization": `Bearer ${this.accessToken}`,
      "Content-Type": "application/json"
    };
  }

  async createResponse(model, messages) {
    let response = await fetch(`${this.baseUrl}/responses`, {
      method: "POST",
      headers: this.getHeaders(),
      body: JSON.stringify({ model, messages })
    });

    // Auto-refresh on 401
    if (response.status === 401) {
      await this.refresh();
      response = await fetch(`${this.baseUrl}/responses`, {
        method: "POST",
        headers: this.getHeaders(),
        body: JSON.stringify({ model, messages })
      });
    }

    if (!response.ok) throw new Error("API request failed");
    return response.json();
  }
}

// Usage
const auth = new FreddyAuth();

// Step 1: Login
const emailKey = await auth.login("user@example.com", "password123");
console.log("Check your email for verification code");

// Step 2: Verify
const verificationCode = 2290; // From email
await auth.verify(emailKey, verificationCode);

// Step 3: Make API calls
const result = await auth.createResponse("gpt-4", [
  { role: "user", content: "Hello!" }
]);
console.log(result);

Token Lifecycle

Token TypeExpirationPurpose
Access Token24 hoursAPI authentication
Refresh Token30 daysRenew access tokens
Email Key5 minutesEmail verification session
Verification Code5 minutesOne-time email code

Security Best Practices

Token Storage

Web Applications:

  • Store tokens in memory or secure HTTP-only cookies
  • Never use localStorage for production apps
  • Clear tokens on logout

Mobile/Desktop:

  • Use secure storage (Keychain, KeyStore)
  • Encrypt tokens at rest
  • Clear on app uninstall

Error Handling

def make_authenticated_request(auth, endpoint, data):
    try:
        response = requests.post(
            f"{auth.base_url}/{endpoint}",
            headers=auth.get_headers(),
            json=data
        )
        
        if response.status_code == 401:
            # Token expired, refresh and retry
            auth.refresh()
            response = requests.post(
                f"{auth.base_url}/{endpoint}",
                headers=auth.get_headers(),
                json=data
            )
        
        response.raise_for_status()
        return response.json()
        
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            print("Authentication failed. Please login again.")
        elif e.response.status_code == 403:
            print("Permission denied.")
        else:
            print(f"Request failed: {e}")
        raise

Common Issues

Verification Code Expired

If the code expires (5 minutes), request a new one:

Endpoint: POST /v1/auth/resend-email

response = requests.post(
    "https://api.aitronos.com/v1/auth/resend-email",
    json={"email_key": email_key}
)

Invalid Credentials

{
  "success": false,
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "Invalid email or password",
    "status": 401
  }
}

Too Many Login Attempts

{
  "success": false,
  "error": {
    "code": "TOO_MANY_LOGIN_ATTEMPTS",
    "message": "Too many login attempts. Please try again later",
    "status": 429,
    "details": {
      "retry_after": 300
    }
  }
}

Alternative: API Key Authentication

For server-to-server or long-lived integrations, use API keys instead:

headers = {
    "api-key": "ak_your_api_key_here",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api.aitronos.com/v1/responses",
    headers=headers,
    json={"model": "gpt-4", "messages": [...]}
)

Get your API key from Freddy Hub.

Next Steps