Most endpoints require authentication using JWT tokens. Include the token in the Authorization header:

Authorization: Bearer <your-jwt-token>

The authentication middleware (authenticateToken) will:

  • Extract user information from the JWT token
  • Set req.user with user data including id, email, and auth_type
  • Store token and user data in session for backend operations
  • Continue as unauthenticated if no valid token is provided

Token Storage & Access

The JWT token is not stored in the database. It lives in three places depending on context:

Context Where the token lives How to access it
Widgets (iframe) Never enters the iframe Use the global agnt SDK (auth is proxied)
Frontend (Vue app, browser) localStorage localStorage.getItem('token') (legacy callsites)
Backend services (Express) req.headers.authorization or req.session.userToken Passed as authToken parameter between services
Orchestrator tools (spawned Node.js) process.env.AGNT_AUTH_TOKEN Automatically injected by the orchestrator

Widgets (browser iframe — use the agnt SDK)

The widget runtime injects a global agnt object into every widget iframe at mount time. The token never enters the iframe — every call is proxied through the parent via postMessage and executed by the parent's authenticated axios instance. Widget code should NOT read localStorage, NOT set Authorization headers, NOT write a getToken() helper. Bypassing the SDK will 401.

// Plugin / native / registry tool execution (most common widget use case)
const joke = await agnt.tool('chucknorris-get-joke', { category: 'dev' });

// Any /api/* endpoint
const agents = await agnt.fetch('/api/agents');
const created = await agnt.fetch('/api/agents', {
  method: 'POST',
  body: { name: 'My Agent' },   // object or JSON.stringify(...) both work
});

// User context, available synchronously
console.log(agnt.user);   // { id, email, name } | null

agnt.tool returns the tool's result directly (not the { success, result } envelope) and throws on tool failure. agnt.fetch returns the parsed response body and throws on non-2xx. agnt.fetch is allowlisted to /api/* paths and to standard HTTP methods (GET/POST/PUT/PATCH/DELETE).

Frontend Vue app (browser, non-widget context)

The Vue app stores the JWT in localStorage under the key token and a global axios interceptor in frontend/src/main.js attaches Authorization: Bearer ... to every outbound request automatically. New code should use the existing axios setup; reach for localStorage.getItem('token') only when bypassing axios.

// Most code — interceptor handles auth automatically
import axios from 'axios';
const { data } = await axios.get('/api/agents');

// Bypass case (raw fetch outside axios):
const token = localStorage.getItem('token');
const res = await fetch('/api/agents', { headers: { Authorization: `Bearer ${token}` } });

Backend Services (Express context)

Inside Express route handlers and services, the token arrives via the request header and is cached in the session by the authenticateToken middleware:

// In a route handler — token from the request
const authToken = req.headers.authorization; // "Bearer <token>"

// Or from session (set automatically by middleware after first auth)
const sessionData = getUserTokenFromSession(req);
// sessionData = { token: "<jwt>", user: { id, email, auth_type } }

// Services receive it as a parameter — just pass it along
const result = await someService.doWork(args, authToken);

Orchestrator Tools (spawned Node.js context)

When the orchestrator's execute_javascript_code tool runs code, it spawns an isolated Node.js process. There is no req, no session, and no localStorage. The orchestrator automatically injects the user's token as an environment variable:

const API = 'http://localhost:3333/api';
const TOKEN = process.env.AGNT_AUTH_TOKEN; // automatically provided

const res = await fetch(API + '/agents/', {
  headers: {
    Authorization: 'Bearer ' + TOKEN,
    'Content-Type': 'application/json',
  },
});
const data = await res.json();
console.log(JSON.stringify(data, null, 2));

Important: Each context has its own way to get the token — don't mix them. localStorage only exists in the browser. req.session only exists in Express handlers. process.env.AGNT_AUTH_TOKEN only exists in orchestrator-spawned processes.


List Connected Providers

GET /api/auth/connected

  • Authentication: Optional — env-sourced providers are install-global and return even for unauthenticated callers; per-user rows are included when a bearer token resolves a userId
  • Description: List OAuth/API-key providers currently connected. Shape mirrors the remote /auth/connected response so the frontend can merge both sources without normalization.
  • Response:
[
  { "providerId": "openai", "connected": true }
]

Notify Provider Changed

POST /api/auth/providers/notify-changed

  • Authentication: None
  • Description: The UI posts provider CRUD to the cloud API directly, so the local backend never sees the write. This endpoint lets a client tell the local backend "a provider changed" so every connected tab refreshes via Socket.IO.
  • Body:
{
  "event": "created | updated | deleted",
  "providerId": "openai"
}
  • Response: { "success": true }400 if event is not one of the three valid values