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/model/response",
 headers=headers,
 json={
 "organization_id": "org_your_org_id",
 "model": "gpt-4o",
 "inputs": [{"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/model/response", {
 method: "POST",
 headers: headers,
 body: JSON.stringify({
 organization_id: "org_your_org_id",
 model: "gpt-4o",
 inputs: [{ role: "user", content: "Hello!" }]
 })
});
curl -X POST "https://api.aitronos.com/v1/model/response" \
 -H "Authorization: Bearer $ACCESS_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "organization_id": "org_your_org_id",
 "model": "gpt-4o",
 "inputs": [{"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, organization_id: str, model: str, inputs: list):
 """Step 3: Make authenticated API request"""
 payload = {"organization_id": organization_id, "model": model, "inputs": inputs}
 response = requests.post(
 f"{self.base_url}/model/response",
 headers=self.get_headers(),
 json=payload,
 )

 # Auto-refresh on 401
 if response.status_code == 401:
 self.refresh()
 response = requests.post(
 f"{self.base_url}/model/response",
 headers=self.get_headers(),
 json=payload,
 )

 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-4o",
 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-4o", [
 { 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. Please try again.",
    "system_message": "Invalid credentials",
    "type": "authentication_error",
    "status": 401,
    "details": {},
    "trace_id": "abc-123-def",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}

Too Many Login Attempts

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

Alternative: API Key Authentication

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

headers = {
 "X-API-Key": "ak_your_api_key_here",
 "Content-Type": "application/json"
}

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

Get your API key from Freddy.

Next Steps