← Back to docs

OpenAI / AI

Language: EN | EN | SV

OpenAI / AI

Tools can use an internal AI engine ("OpenAI Engine") to analyze content and power platform features — without ever exposing API keys in the frontend.

This is a user manual that describes:

  • The OpenAI Engine admin UI
  • The difference between the SocialGPT token flow and the broader direct OpenAI-backed Tools access flow
  • The URL analysis API endpoint
  • Permissions and authentication
  • How to choose the right endpoint for a real integration
  • How to avoid common 401, 403, and 422 mistakes

Two different AI access layers on the platform

The platform now makes a deliberate distinction between:

  1. SocialGPT / Social Media Tools access
  2. Direct OpenAI-backed Tools access

That distinction matters because they are not the same thing.

Important: POST /api/ai/socialgpt/respond is not the main/general-purpose Tools "respond" endpoint. It exists for the SocialGPT / Social Media Tools client contract. Internal runtimes such as IRCWatch should normally use POST /api/ai/internal/respond instead.

SocialGPT / Social Media Tools access

  • Uses bearer tokens that have the access scope ai.socialgpt (ai.client is kept as a legacy alias)
  • The built-in SocialGPT generator still creates a dedicated token row with the label/provider provider_socialgpt (legacy tools_ai_bearer rows still work), but the runtime auth decision now follows the token scope instead of the provider name itself
  • Is meant for the SocialGPT browser/client workflow and related Social Media Tools usage through Tools
  • Is configured from the Social Media Tools start page, but that page is no longer the home for the direct OpenAI access-request form

Direct OpenAI-backed Tools access

  • Is the broader platform-level access gate for direct OpenAI-backed Tools endpoints and internal OpenAI-forwarder token workflows
  • Lives under My API Keys (/keys/mine) and the admin review queue /admin/openai
  • Is what you request when you need access outside the dedicated SocialGPT token flow

Web: OpenAI Engine (admin)

URL:

  • /admin/openai

Requirements:

  • Logged-in user (web session)
  • Permission: openai.manage

In the web UI you can:

  • Enable/disable the engine (Enabled)
  • Set global policy (default model, allowlist, token caps, rate limits)
  • Create/update Prompt profiles
  • Run Test prompt to verify that the provider + config works (server-side)

Dynamic model dropdowns

The model dropdowns in /admin/openai are no longer hardcoded.

  • Tools fetches the provider catalog from OpenAI GET /v1/models server-side
  • the result is filtered down to chat-usable model ids
  • if allowed_models is configured, the dropdown is intersected with that allowlist
  • if live discovery fails, Tools falls back to the configured/default models so the UI stays usable

This means the admin model picker is based on what the current provider key can actually use, without exposing any key in the browser.

What does “Enabled” mean?

  • Enabled = off: AI feature endpoints should be unavailable (often 503, depending on endpoint).
  • Enabled = on: the engine can be used (as long as a global provider key exists).

Note: provider keys are managed under API Keys and are never shown in plain text.

Audit visibility for operators

If Slack audit forwarding is enabled for OpenAI / SocialGPT request categories, audit entries now also include:

  • resolved user identity (user_id, and name/email when available)
  • request IP / method / path metadata
  • a readable error_reason when an upstream/provider request fails

This makes it easier for operators to see who triggered an AI request and why a failed request was rejected.

Personal Tools AI tokens

Users who are allowed to use Tools AI can create one or more personal bearer tokens in Tools:

  • Go to My API Keys: /keys/mine
  • If your account is new, submit an OpenAI access request there first and wait for admin approval
  • Use Tools OpenAI forwarder tokens to create separate named tokens for different internal jobs/clients
  • Each token can be rotated or killed separately later
  • The token value is shown once and stored server-side
  • The generic Add new key form on /keys/mine now shows the known Tools access scopes as a checkbox list instead of expecting users to guess the scope names manually
  • The same key form can now also generate local/internal-style public and secret values directly in the browser flow when you need a Tools-managed token skeleton instead of pasting a third-party credential

These Tools-side AI client tokens are:

  • client/receiver tokens used towards Tools
  • valid for Tools AI endpoints that already require approved OpenAI access for the owning user
  • not the same thing as the real upstream OpenAI provider key
  • now authorized by explicit token access scopes (ai.socialgpt, ai.internal, etc.), so the provider/name is only a label/category

This makes it possible to keep one token per assignment (for example one internal automation, one batch job, one research client) without sharing the same bearer everywhere.

Tools still also has a separate dedicated SocialGPT token flow:

  • the built-in SocialGPT token generator still creates a provider_socialgpt token row for convenience
  • legacy tools_ai_bearer tokens still work for compatibility
  • those generated SocialGPT tokens are simply preconfigured with the scope ai.socialgpt

Use it like this:

Authorization: Bearer <token>

Each token works only for the endpoints covered by its own access scopes and is tied to your user account + permissions.

The current built-in access scopes shown in My API Keys are:

  • ai.socialgpt
  • ai.internal
  • whisper.api
  • mail-support-assistant.relay
  • sms.send
  • sms.gateway

Tools now also supports other personal per-system tokens (for example provider_ircwatch or provider_mail_support_assistant) as long as they are:

  • personal / non-global
  • active
  • marked as AI-capable (is_ai=1) and/or given the explicit scope ai.socialgpt

Important distinction:

  • these AI-capable tokens are client/receiver tokens used towards Tools
  • provider_openai is the upstream provider secret used towards OpenAI and is never treated as an AI receiver token

Integration quick start

If you are integrating a client against Tools AI, start here.

Step 1: choose the correct endpoint type

Do not start by pasting one random AI URL into a client and hoping it behaves like the public OpenAI API.

Tools currently has multiple AI-related endpoints with different contracts:

Use case Endpoint Token scope Important note
SocialGPT-style reply / verify / modify workflow POST /api/ai/socialgpt/respond ai.socialgpt This is a Tools-specific contract, not an OpenAI-compatible one
Internal runtime / assistant / automation client POST /api/ai/internal/respond ai.internal Requires client_slug and at least context or user_prompt
IRCWatch / server-side bot runtime POST /api/ai/internal/respond ai.internal Prefer an internal client slug such as ircwatch; do not treat SocialGPT as the default runtime endpoint
Analyze one remote URL server-side POST /api/ai/url/analyze approved OpenAI access for the authenticated user Not meant as a generic chat endpoint
Validate a SocialGPT-style bearer token only GET /api/social-media-tools/extension/validate-token ai.socialgpt Checks the token, not whether the user may actually run OpenAI

Step 2: confirm whether your client expects an OpenAI-compatible API

This is the most common integration mistake.

Tools endpoints such as:

  • POST /api/ai/socialgpt/respond
  • POST /api/ai/internal/respond

are not drop-in OpenAI /v1/chat/completions replacements.

That means:

  • a generic “AI Provider URL” textbox in another client may still fail even when the token is valid
  • if that client only knows how to send OpenAI-style JSON, Tools will answer with validation errors instead of usable completions
  • in particular, POST /api/ai/internal/respond requires Tools-specific fields such as client_slug

If your client cannot control the JSON request body, it may need an adapter/proxy instead of direct connection to the internal Tools endpoint.

Step 3: verify the token scope and the owning user's access

For bearer-token integrations, two separate checks matter:

  1. The token itself must have the correct scope
  2. The token owner must still be allowed to use OpenAI-backed Tools features

Examples:

  • a token can have ai.internal and still be rejected with 403 if the owning user lacks approved OpenAI access
  • a token can be valid in validate-token and still fail on a real AI request because the token owner lacks provider_openai

Step 4: test with a raw HTTP request before blaming the client UI

Before debugging one browser extension, Copilot-style form, or desktop settings page, test the exact endpoint manually with curl or another raw HTTP client.

That immediately answers:

  • whether the token is accepted
  • whether the user has OpenAI access
  • whether the payload shape is valid
  • whether the endpoint is the right one for the client type

Which URL should I use?

For a SocialGPT-style client

Use:

  • POST /api/ai/socialgpt/respond

when the client can send Tools-specific fields such as:

  • context
  • user_prompt
  • modifier
  • request_mode
  • optional client metadata (client_name, client_version, client_platform)

For an internal assistant / runtime / automation client

Use:

  • POST /api/ai/internal/respond

when the client can send a Tools-specific JSON body that includes:

  • client_slug
  • and at least one of context or user_prompt

This is the normal choice for Tools-side runtimes such as IRCWatch, cron jobs, background workers, and other non-SocialGPT integrations.

For a generic OpenAI-compatible chat client

Do not assume that POST /api/ai/internal/respond is the right URL.

If the client only knows how to send OpenAI-native payloads such as messages[], input, or standard Chat Completions fields, you currently need one of these instead:

  • a dedicated Tools-side OpenAI-compatible adapter endpoint
  • or a small proxy that transforms the incoming request into the Tools contract

Without that adapter layer, the client may fail with 422 Unprocessable Content even though the token is correct.

API: URL Analyze

OpenAI access requests (user workflow)

Regular new users are no longer allowed to use OpenAI-backed Tools features automatically just because a daily budget exists.

User flow:

  • Open My API Keys: /keys/mine
  • If you open /openai/access/request directly in the browser, Tools now redirects you back to the built-in request card on My API Keys instead of leaving you on a generic error page
  • The same redirect safety also accepts the common typo /openai/access/requst
  • If your account does not yet have OpenAI access, use the built-in request form and explain what you need
  • The Social Media Tools setup page now links here when you need platform-wide OpenAI access outside the SocialGPT token flow
  • An admin reviews the request from /admin/openai
  • After approval, the account receives the real provider_openai access right and can then create one or more Tools OpenAI forwarder tokens in My API Keys

Admin flow:

  • Open /admin/openai
  • Review the OpenAI access requests table
  • Approve or reject requests inline
  • When one or more OpenAI access requests are still pending, Tools now shows a small fixed reminder on normal signed-in pages for admins/reviewers, with a direct link back to /admin/openai
  • Rejecting a request also removes any existing personal AI receiver tokens for that user (including dedicated provider_tools_openai forwarder tokens, not only the legacy Tools AI bearer row)

API: URL Analyze

Endpoint:

  • POST /api/ai/url/analyze

Purpose:

  • You send a URL (and optionally a question)
  • Tools fetches and sanitizes the content server-side
  • OpenAI Engine analyzes the text using a selected prompt profile

Request

Form data or JSON:

  • url (required) — URL to analyze
  • question (optional) — analysis focus/question
  • profile (optional) — prompt profile name (default: URL Analyzer if it exists, otherwise the engine falls back to a minimal default profile)

Response

JSON:

  • ok — true/false
  • request_id — internal request id
  • latency_ms — approximate latency
  • model — model used
  • response — model output (if ok)
  • error — error message (if ok=false)

Auth & Permissions

To use the endpoint you need:

  • Authentication: if no user exists — 401 Unauthenticated
  • Admin (is_admin=1) — always allowed
  • Non-admin — requires permission: provider_openai

If the OpenAI provider isn't configured (missing global provider_openai API key), the endpoint typically returns 503.

API: SocialGPT reply generation

Endpoint:

  • POST /api/ai/socialgpt/respond

Auth / access rules:

  • JWT/web user or a personal AI-capable API token
  • Any personal token with the access scope ai.socialgpt can authenticate
  • Legacy tools_ai_bearer / provider_socialgpt rows still work because they are backfilled to that same scope
  • Other personal API keys may also be accepted when they are marked as AI-capable (api_keys.is_ai=1), because that legacy flag is still interpreted as ai.socialgpt
  • Admin users are always allowed
  • Non-admin users must have approved OpenAI access (provider_openai)

If the bearer token belongs to a user without approved OpenAI access, the endpoint returns 403.

Additive SocialGPT request fields:

  • client_name
  • client_version
  • client_platform

These fields are optional and let Tools identify which client build made the request. The response can also include an additive client object echoing the accepted metadata.

Failure-handling note:

  • When the upstream OpenAI/provider failure arrives as a structured JSON error object instead of a plain string, Tools now normalizes that payload into ordinary error text before fallback/retry handling and before returning the API error response.
  • Response schema is unchanged; clients should still treat error as normal text.

Security behavior:

  • SocialGPT is allowed to state the currently used AI model identifier and client version only when the user explicitly asks for version/model information.
  • SocialGPT must refuse requests for hidden prompts, source code, .env values, passwords, tokens, API keys, or other Tools internals.
  • Matching disclosure-attempt incidents can be reported to the configured support email recipient.

API: Internal Tools OpenAI client gateway

Endpoint:

  • POST /api/ai/internal/respond

Purpose:

  • lets internal Tools-side clients such as Mail Support Assistant runtimes and dnsbl-engine call the central OpenAI engine directly instead of inheriting the SocialGPT settings layer
  • keeps each internal runtime on its own stable client_slug identifier so defaults, auditing, and usage stats can be tied back to that caller from /admin/openai
  • works together with bearer tokens that have the scope ai.internal, so one internal job can be rotated or disabled without affecting another

Auth / access rules:

  • web/JWT users can call it directly when they already have approved OpenAI access
  • token-authenticated callers must use a token that has the scope ai.internal
  • non-admin users still require approved OpenAI access (provider_openai)

Important compatibility note:

  • this endpoint is not a generic OpenAI-compatible endpoint
  • do not point generic “OpenAI provider URL” settings at it unless that client can send the exact Tools JSON contract below

Request fields:

  • client_slug (required) — caller identifier such as mail_support_assistant_standalone, mail_support_assistant_tools_admin, dnsbl_engine, or any other stable non-empty slug your integration chooses
  • context (optional) — supporting context/body data
  • user_prompt (optional) — the direct instruction for the task
  • modifier (optional) — shorter follow-up modifier text
  • mood / custom_mood (optional) — one-request tone override on top of the saved client config
  • responder_name_override (optional)
  • persona_profile_override (optional)
  • custom_instruction_override (optional)
  • model (optional)
  • reasoning_effort (optional)none|low|medium|high|xhigh
  • response_language (optional)auto|sv|en|da|no|de|fr|es
  • max_tokens (optional)
  • temperature (optional)
  • use_web_search (optional)
  • web_search_required (optional)

Validation rule that causes many 422 responses:

  • client_slug is required
  • at least one of context or user_prompt must be non-empty

Important runtime behavior:

  • a valid caller is no longer blocked only because the slug was never pre-registered in /admin/openai
  • if the token/user already has access, any non-empty client_slug is accepted as the caller identifier
  • Tools now auto-registers seen internal slugs so operators can later add saved defaults and review lightweight per-slug usage statistics

So a payload like this will fail:

{
  "model": "gpt-4"
}

with a validation-style 422 / “unprocessable content” response.

Minimum valid internal request

{
  "client_slug": "client_browser_companion",
  "user_prompt": "Reply with ok",
  "context": "Connection test"
}

Example: internal assistant request

curl -X POST "https://tools.tornevall.net/api/ai/internal/respond" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "client_slug": "client_browser_companion",
    "context": "Temporary demo feature",
    "user_prompt": "Reply with ok",
    "model": "gpt-4o",
    "response_language": "en"
  }'

Success response includes:

  • ok
  • request_id
  • model
  • response
  • usage
  • used_fallback_model
  • client (slug, name, description)
  • applied_settings (responder_name, persona_profile_excerpt, custom_instruction_excerpt, mood, response_language, reasoning_effort)
  • additive web_search metadata (requested, required, used, citations[])

Admin UI note:

  • /admin/openai now also contains a centralized Internal AI clients section where operators can edit saved defaults for each internal client_slug, including slugs that were first auto-registered from live traffic.

How do I instruct one internal assistant?

For internal assistant-style clients, the main control point is not the URL itself.

The behavior comes from two layers:

  1. the saved server-side config for the selected client_slug (when one exists)
  2. optional one-request override fields

That means the normal way to “instruct Copilot for this assistant” is:

  • pick one stable client_slug for that runtime
  • optionally let that slug appear once and then refine it in /admin/openai if you want saved defaults/stat tracking there
  • configure the saved defaults there
  • optionally send per-request overrides such as:
    • responder_name_override
    • persona_profile_override
    • custom_instruction_override
    • mood
    • response_language

If the external client UI does not let you send these extra fields, then the integration is limited by that client UI, not by the Tools token itself.

Troubleshooting

401 Unauthenticated

Usually means:

  • no bearer token was sent
  • the token value is wrong
  • the token is inactive
  • the token does not have the required scope for that endpoint

403 Forbidden

Usually means:

  • the token is valid, but the token owner lacks approved OpenAI access (provider_openai)
  • or the user is otherwise not allowed to use that endpoint

This is not the same thing as “bad token”.

422 Unprocessable Content

Usually means:

  • the endpoint was reached successfully
  • auth probably passed far enough to hit validation
  • but the JSON body is missing required Tools fields

For POST /api/ai/internal/respond, the usual cause is one of these:

  • missing client_slug
  • both context and user_prompt are empty
  • an external client is sending a generic OpenAI payload instead of the Tools contract

“My token validates, but real requests still fail”

That is expected when you only tested:

  • GET /api/social-media-tools/extension/validate-token

because that endpoint only verifies the token itself.

It does not prove that the user may execute OpenAI-backed requests right now.

“Which endpoint should I test first?”

For a new integration, test in this order:

  1. validate token / auth model
  2. run a raw curl request against the real target endpoint with a minimum valid payload
  3. only then wire the same values into the GUI/client you actually want to use

Documentation sketch: future OpenAI-compatible adapter endpoint

This section is intentionally a design sketch only.

It documents one recommended direction for a future Tools-side compatibility adapter so external clients that only understand the OpenAI-style request format can still talk to Tools without sending the current Tools-specific client_slug / context / user_prompt payload shape directly.

This endpoint does not exist yet as a public contract.

Why an adapter is useful

Some clients only support fields such as:

  • model
  • messages[]
  • temperature
  • max_tokens

and a single “provider URL” textbox.

Those clients cannot easily call:

  • POST /api/ai/internal/respond

because the internal Tools endpoint expects fields such as:

  • client_slug
  • context
  • user_prompt

An adapter endpoint would solve that mismatch.

Recommended adapter shape

Suggested future endpoint:

  • POST /api/openai/v1/chat/completions

Suggested auth model:

  • bearer token with scope ai.internal
  • or a narrower future scope dedicated to the adapter if you later want to separate it from the raw internal gateway

Suggested backend behavior:

  1. accept an OpenAI-style chat payload
  2. resolve the internal client_slug from one of these sources:
    • explicit request field such as additive client_slug
    • additive header such as X-Tools-Client-Slug
    • token-level default configured in Tools later
    • final safe fallback such as generic_openai_compat_client
  3. flatten messages[] into the internal Tools request shape:
    • earlier system / developer / assistant history → context
    • latest user message → user_prompt
  4. forward supported generation fields:
    • model
    • temperature
    • max_tokens
  5. call the existing POST /api/ai/internal/respond service layer internally
  6. convert the Tools response back into an OpenAI-compatible response object

Suggested minimum accepted request body

{
  "model": "gpt-4o",
  "messages": [
    {
      "role": "user",
      "content": "Summarize this page in three bullets."
    }
  ]
}

Suggested additive Tools-specific compatibility fields

These are optional ideas for a future adapter. They are not active public API fields today.

{
  "client_slug": "generic_openai_compat_client",
  "response_language": "en",
  "tools_context": "Temporary browser-side assistant session"
}

Suggested response shape

For maximum compatibility, return something structurally close to OpenAI Chat Completions, for example:

{
  "id": "tools-chatcmpl-123",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "gpt-4o",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 100,
    "completion_tokens": 40,
    "total_tokens": 140
  }
}

Recommended validation/error mapping

If this adapter is implemented later, keep the error semantics explicit:

  • 401 → missing/invalid token
  • 403 → token accepted, but the token owner lacks OpenAI access
  • 422 → missing messages[], missing usable user message, or missing client resolution
  • 503 → Tools OpenAI engine/provider unavailable

Recommended implementation location

If implemented later, the cleanest direction is usually:

  • new controller method near app/Http/Controllers/AiGatewayController.php
  • route in routes/api.php
  • reuse of InternalOpenAiClientService
  • one small request/response mapper layer instead of duplicating the OpenAI engine logic

Generic code snippets for internal assistant integrations

The snippets below are intentionally generic. They do not depend on one specific internal client such as client_browser_companion.

Replace placeholder values like:

  • YOUR_API_TOKEN
  • YOUR_CLIENT_SLUG
  • YOUR_TEXT_HERE

with values that match your own Tools setup.

Minimum valid internal request (JSON)

{
  "client_slug": "YOUR_CLIENT_SLUG",
  "user_prompt": "YOUR_TEXT_HERE",
  "context": "Optional supporting context"
}

curl example

curl -X POST "https://tools.tornevall.net/api/ai/internal/respond" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "client_slug": "generic_internal_assistant",
    "context": "Optional page or workflow context.",
    "user_prompt": "Summarize the important actions from this page.",
    "model": "gpt-4o",
    "response_language": "en"
  }'

JavaScript / fetch example

const response = await fetch("https://tools.tornevall.net/api/ai/internal/respond", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_API_TOKEN"
  },
  body: JSON.stringify({
    client_slug: "generic_internal_assistant",
    context: "Optional page or workflow context.",
    user_prompt: "Summarize the important actions from this page.",
    model: "gpt-4o",
    response_language: "en"
  })
});

const data = await response.json();
console.log(data);

PowerShell example

$headers = @{
  "Content-Type"  = "application/json"
  "Authorization" = "Bearer YOUR_API_TOKEN"
}

$body = @{
  client_slug       = "generic_internal_assistant"
  context           = "Optional page or workflow context."
  user_prompt       = "Summarize the important actions from this page."
  model             = "gpt-4o"
  response_language = "en"
} | ConvertTo-Json

Invoke-RestMethod \
  -Method Post \
  -Uri "https://tools.tornevall.net/api/ai/internal/respond" \
  -Headers $headers \
  -Body $body

Python example

import requests

response = requests.post(
    "https://tools.tornevall.net/api/ai/internal/respond",
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer YOUR_API_TOKEN",
    },
    json={
        "client_slug": "generic_internal_assistant",
        "context": "Optional page or workflow context.",
        "user_prompt": "Summarize the important actions from this page.",
        "model": "gpt-4o",
        "response_language": "en",
    },
    timeout=60,
)

print(response.status_code)
print(response.json())

Generic /admin/openai instruction template for internal assistants

If you are creating one internal assistant profile in /admin/openai, this generic starter template is often a better first step than hardcoding one client-specific persona immediately.

Suggested client intent

Use this style for assistants that should:

  • summarize supplied context
  • explain visible content or workflow state
  • rewrite text clearly
  • suggest next actions
  • stay practical and concise

Suggested responder style

  • concise before comprehensive
  • practical before theoretical
  • explain what to do next, not only what something means
  • avoid unnecessary filler
  • do not pretend to know hidden context that the client has not supplied
  • when information is missing, say exactly what is missing

Suggested custom instruction text

You are a practical internal assistant for Tools-connected clients.

Your job is to help the user understand the supplied context, summarize important information, rewrite or clarify text, and suggest useful next actions.

Priorities:
1. Be clear and operationally useful.
2. Prefer short, direct answers unless the user explicitly asks for more detail.
3. When summarizing, focus on the most relevant facts, decisions, blockers, deadlines, and action items.
4. If the supplied context is incomplete, say what is missing instead of inventing details.
5. Do not claim to have clicked, fetched, verified, or inspected anything beyond the supplied context.
6. If the user asks for a rewrite, preserve the meaning but improve structure, tone, and clarity.
7. If the user asks what to do next, provide a short ordered action list.

Tone:
- calm
- competent
- helpful
- not overly chatty

Avoid:
- hype
- vague consultant language
- pretending that uncertain information is verified fact
- long introductions or closing filler unless the user asks for a more formal style

Suggested default mood

One good generic starting value in /admin/openai is:

  • clear, concise, practical

Suggested response language behavior

  • use the same language as the user's visible request when possible
  • if the incoming request is mixed, prefer the language used in the actual prompt/question

API: Extension smoke test vs token validation

Related extension endpoints:

  • GET /api/social-media-tools/extension/validate-token
  • GET /api/social-media-tools/extension/test
  • POST /api/social-media-tools/extension/test

Important difference:

  • validate-token only verifies that the supplied personal AI-capable token itself is valid
  • test performs a real OpenAI-backed smoke test and therefore requires approved OpenAI access for non-admin users

This lets clients distinguish "the token belongs to a real user" from "that user is actually allowed to run OpenAI requests right now".

Example

curl -X POST "https://tools.tornevall.net/api/ai/url/analyze" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  -d '{
    "url": "https://example.com",
    "question": "What is this page about?",
    "profile": "URL Analyzer"
  }'

Security

  • No keys are exposed in the frontend.
  • URL content is fetched server-side with SSRF protections (private ranges blocked, size limits, timeouts, redirect limits).
  • System/developer instructions are defined by prompt profiles, not by the client.

API: Social Media extension model catalog

Endpoint:

  • GET /api/social-media-tools/extension/models

Purpose:

  • returns the backend-discovered model list for the authenticated Tools bearer token
  • gives the Chrome extension a dynamic model dropdown without calling OpenAI directly from the extension
  • reuses the same provider key resolution rules as the rest of Tools (personal OpenAI key if present, otherwise global)

Response fields include:

  • models — array of available model options
  • default_model — effective default model for this user/context
  • source — whether the list came from live provider discovery or a configured fallback
  • warning — optional fallback/discovery message