Copy this entire documentation and paste into any AI assistant to build apps with FranzAI Bridge

Back to FranzAI Bridge

FranzAI Bridge Documentation

Complete reference for building browser AI apps. No backend needed.

What is FranzAI Bridge?

FranzAI Bridge is a Chrome extension that lets you call AI APIs (OpenAI, Anthropic, Google, Mistral) directly from any webpage. It solves two problems:

Requirements

Quick Start

1. Check if extension is installed

const bridge = window.franzai;
if (bridge) {
  const { ok, version } = await bridge.ping();
  console.log('Bridge ready:', version);
} else {
  console.log('Extension not installed');
}

2. Make an API call

// Just use fetch() - the extension handles everything
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: 'gpt-5-mini',
    messages: [{ role: 'user', content: 'Hello!' }]
  })
});

const data = await response.json();
console.log(data.choices[0].message.content);
That's it! The extension automatically injects your API key. No backend server needed.

Available Models (Verified January 2026)

OpenAI Models

ModelDescription
gpt-5.2Newest, best for complex tasks and coding
gpt-5-miniFaster, cost-efficient
gpt-5-nanoFastest, most affordable
gpt-5.1-codex-miniOptimized for code generation

Anthropic Claude Models

ModelDescription
claude-opus-4-5Most capable, best for complex tasks
claude-sonnet-4-5Balanced performance and cost
claude-haiku-4-5Fastest, most affordable

Use the aliases above (they point to the latest dated version in each family).

Google Gemini Models

ModelDescription
gemini-3-pro-previewLatest, most capable (preview)
gemini-3-flash-previewFast next-gen model (preview)
gemini-2.5-proHigh capability (stable)
gemini-2.5-flashFast and efficient (stable)
gemini-2.5-flash-liteLightweight, fastest (stable)

Mistral Models

ModelDescription
mistral-large-latestFlagship model (Mistral Large 3)
mistral-medium-latestBalanced (Mistral Medium 3.1)
mistral-small-latestFast and efficient (Mistral Small 3.2)
ministral-8b-latestSmall, edge-optimized
codestral-latestOptimized for code completion
devstral-latestOptimized for code agents

Provider Examples

OpenAI

const response = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: 'gpt-5-mini',
    messages: [{ role: 'user', content: 'Hello!' }]
  })
});
const data = await response.json();
console.log(data.choices[0].message.content);

Anthropic Claude

const response = await fetch('https://api.anthropic.com/v1/messages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'anthropic-version': '2023-06-01'
  },
  body: JSON.stringify({
    model: 'claude-haiku-4-5',
    max_tokens: 1024,
    messages: [{ role: 'user', content: 'Hello!' }]
  })
});
const data = await response.json();
console.log(data.content[0].text);

Google Gemini

const response = await fetch(
  'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      contents: [{ parts: [{ text: 'Hello!' }] }]
    })
  }
);
const data = await response.json();
console.log(data.candidates[0].content.parts[0].text);

Mistral

const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: 'mistral-small-latest',
    messages: [{ role: 'user', content: 'Hello!' }]
  })
});
const data = await response.json();
console.log(data.choices[0].message.content);

API Reference

window.franzai.ping()

Check if the extension is installed and ready.

const { ok, version } = await window.franzai.ping();
// ok: true if extension is ready
// version: string like "2.0.53"

window.franzai.fetch(url, options)

Explicit bridge call. Same as regular fetch() but only routes through the extension when the domain is enabled (still ignores per-request franzai.mode overrides).

const response = await window.franzai.fetch(
  'https://api.openai.com/v1/chat/completions',
  { method: 'POST', body: '...' }
);

window.franzai.setMode(mode)

Control when the bridge intercepts requests.

ModeBehavior
'auto'Only intercept cross-origin requests (default)
'always'Route ALL requests through bridge
'off'Disable bridge, use native fetch
window.franzai.setMode('always');

window.franzai.getMode()

Get current bridge mode.

const mode = window.franzai.getMode(); // 'auto' | 'always' | 'off'

window.franzai.version

Current bridge version string (same as ping()).

const version = window.franzai.version; // "2.0.53"

window.franzai.isKeySet(keyName)

Check if a key is configured in the extension (does not expose the key).

const hasKey = await window.franzai.isKeySet('OPENAI_API_KEY');

window.franzai.hasApiKey(keyName)

Alias for isKeySet. Checks if a specific API key is configured.

const hasKey = await window.franzai.hasApiKey('OPENAI_API_KEY');

window.franzai.keys

Array of configured key names (values are never exposed).

const keys = window.franzai.keys; // ['OPENAI_API_KEY', 'MISTRAL_API_KEY']

window.franzai.getStatus()

Get bridge readiness for the current domain.

FieldDescription
installedExtension present
versionBridge version string
domainEnabledDomain toggle state
domainSourceuser | meta | default
originAllowedOrigin policy check (if enforced)
hasApiKeysAt least one API key configured
readyBridge ready to intercept
reasonHuman-readable status message
const status = await window.franzai.getStatus();

Per-request mode override (fetch / Request)

Override bridge routing for a single request when using window.fetch.

Note: This does not affect window.franzai.fetch, but it will still be blocked if the domain is disabled.

// One-off bypass
await fetch('/api/data', { franzai: { mode: 'off' } });

// Or via Request
const req = new Request('/api/data', { franzai: { mode: 'off' } });
await fetch(req);

Google OAuth API

FranzAI Bridge provides OAuth authentication for Google APIs like Search Console and Analytics. Tokens are stored locally and auto-refreshed.

franzai.google.auth(scopes)

Authenticate with Google. Shows consent screen on first call, silent on subsequent calls if scopes match.

// Request Search Console access
await franzai.google.auth(['webmasters.readonly']);

// Request multiple scopes
await franzai.google.auth(['webmasters.readonly', 'analytics.readonly']);

franzai.google.logout()

Clear stored OAuth tokens. Next auth() call will show account picker again.

await franzai.google.logout();

franzai.google.fetch(url, options)

Fetch from Google APIs with automatic OAuth token injection. Token is refreshed automatically if expired.

// List Search Console sites
const response = await franzai.google.fetch(
  'https://searchconsole.googleapis.com/webmasters/v3/sites'
);
const data = await response.json();
console.log(data.siteEntry);

franzai.google.isAuthenticated

Boolean indicating if user is currently authenticated.

if (franzai.google.isAuthenticated) {
  console.log('Logged in as:', franzai.google.email);
}

franzai.google.email

Email address of authenticated user, or null if not authenticated.

const email = franzai.google.email; // "[email protected]" or null

franzai.google.scopes

Array of currently authorized scopes.

const scopes = franzai.google.scopes; // ['webmasters.readonly']

franzai.google.hasScopes(scopes)

Check if specific scopes are already authorized (without prompting).

const hasAccess = await franzai.google.hasScopes(['analytics.readonly']);

Supported Scopes (v1)

ShorthandFull ScopeAccess
webmasters.readonlyhttps://www.googleapis.com/auth/webmasters.readonlySearch Console (read)
analytics.readonlyhttps://www.googleapis.com/auth/analytics.readonlyAnalytics (read)
analyticshttps://www.googleapis.com/auth/analyticsAnalytics (read/write)

Google OAuth Example

// Complete example: Fetch Search Console performance data
async function getSearchPerformance(siteUrl) {
  // Authenticate if needed
  if (!franzai.google.isAuthenticated) {
    await franzai.google.auth(['webmasters.readonly']);
  }

  // Fetch performance data
  const response = await franzai.google.fetch(
    `https://searchconsole.googleapis.com/webmasters/v3/sites/${encodeURIComponent(siteUrl)}/searchAnalytics/query`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        startDate: '2025-12-01',
        endDate: '2025-12-31',
        dimensions: ['query'],
        rowLimit: 10
      })
    }
  );

  const data = await response.json();
  return data.rows;
}

// Usage
const queries = await getSearchPerformance('sc-domain:example.com');
console.log('Top queries:', queries);
Token Refresh: Access tokens expire after 1 hour. The extension automatically refreshes them using the stored refresh token - your code doesn't need to handle this.

Configuration

Domain enablement

The bridge only intercepts on domains you enable in the extension's Domains tab. You can also enable a domain by adding this meta tag to your page:

<meta name="franzai-bridge" content="enabled">

Valid values: enabled, enabled-by-default, true. Meta-enabled domains show up with source meta.

ENV Variables (API Keys)

Add API keys in the extension's Settings panel. Each key has three properties:

PropertyDescription
NameVariable name (e.g., OPENAI_API_KEY)
TargetDomain where key is sent (e.g., api.openai.com)
ValueYour actual API key

Built-in keys (auto-configured targets):

Security: Keys are target-restricted. A key for api.openai.com will ONLY be sent to that domain - never to other sites.

Complete Example: Multi-Provider Chat

A full working example you can copy and use:

// Multi-provider AI chat function
async function chat(provider, message) {
  const configs = {
    openai: {
      url: 'https://api.openai.com/v1/chat/completions',
      body: { model: 'gpt-5-mini', messages: [{ role: 'user', content: message }] },
      extract: data => data.choices[0].message.content
    },
    anthropic: {
      url: 'https://api.anthropic.com/v1/messages',
      headers: { 'anthropic-version': '2023-06-01' },
      body: { model: 'claude-haiku-4-5', max_tokens: 1024, messages: [{ role: 'user', content: message }] },
      extract: data => data.content[0].text
    },
    gemini: {
      url: 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent',
      body: { contents: [{ parts: [{ text: message }] }] },
      extract: data => data.candidates[0].content.parts[0].text
    },
    mistral: {
      url: 'https://api.mistral.ai/v1/chat/completions',
      body: { model: 'mistral-small-latest', messages: [{ role: 'user', content: message }] },
      extract: data => data.choices[0].message.content
    }
  };

  const config = configs[provider];
  const response = await fetch(config.url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', ...config.headers },
    body: JSON.stringify(config.body)
  });

  if (!response.ok) throw new Error(`API error: ${response.status}`);
  return config.extract(await response.json());
}

// Usage
const reply = await chat('openai', 'What is 2+2?');
console.log(reply);

Error Handling

async function safeChat(message) {
  // Check extension first
  if (!window.franzai) {
    throw new Error('FranzAI Bridge extension not installed');
  }

  try {
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        model: 'gpt-5-mini',
        messages: [{ role: 'user', content: message }]
      })
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error?.message || `HTTP ${response.status}`);
    }

    const data = await response.json();
    return data.choices[0].message.content;
  } catch (err) {
    console.error('Chat failed:', err.message);
    throw err;
  }
}

Troubleshooting

ProblemSolution
Extension not detectedReload page, check extension is enabled in chrome://extensions
API key not workingCheck key name matches exactly (e.g., OPENAI_API_KEY)
"Bridge disabled for this domain"Enable the domain in the Domains tab or add <meta name="franzai-bridge" content="enabled"> to your page
"Destination not allowed"Add the API domain to Allowed Destinations in Settings
401 UnauthorizedAPI key is invalid or expired
429 Rate LimitedToo many requests, wait and retry

Limitations

Architecture

The extension uses Chrome's world: "MAIN" content script injection to guarantee the fetch hook is installed before any page script runs. This eliminates race conditions entirely.

// Chrome injects this SYNCHRONOUSLY at document_start
// before any <script> tags in the page execute

// 1. MAIN world: hooks window.fetch (same context as page)
// 2. ISOLATED world: relays messages to background
// 3. Background: makes actual fetch (no CORS), injects API keys

// The hook is protected with Object.defineProperty
// - configurable: false (cannot be deleted)
// - writable: false (cannot be overwritten)