Skip to main content

AI

Endpoints for configuring AI providers and generating content with large language models.

Endpoints

MethodPathPermissionDescription
GET/sites/{site_id}/ai/configAdminRetrieve AI configuration
PUT/sites/{site_id}/ai/configAdminCreate or update AI configuration
DELETE/sites/{site_id}/ai/configAdminRemove AI configuration
POST/sites/{site_id}/ai/testAdminTest provider connection
POST/sites/{site_id}/ai/generateAuthorGenerate content
POST/sites/{site_id}/ai/modelsAdminList available models

Get AI Configuration

Returns the AI provider configuration for a site. The API key is always returned in masked form.

curl https://your-site.com/api/v1/sites/{site_id}/ai/config \
-H "X-API-Key: your_api_key"

Response -- 200 OK

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"site_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"provider_name": "OpenAI",
"base_url": "https://api.openai.com",
"api_key_masked": "sk-abc1...xyz9",
"model": "gpt-4o-mini",
"temperature": 0.7,
"max_tokens": 1024,
"system_prompts": {},
"updated_at": "2026-03-08T10:30:00Z"
}

Returns 404 if no AI configuration exists for the site.

Create or Update AI Configuration

Creates a new configuration or updates the existing one (upsert). The API key is encrypted with AES-256-GCM before storage.

curl -X PUT https://your-site.com/api/v1/sites/{site_id}/ai/config \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"provider_name": "OpenAI",
"base_url": "https://api.openai.com",
"api_key": "sk-...",
"model": "gpt-4o-mini",
"temperature": 0.7,
"max_tokens": 1024,
"system_prompts": {
"seo": "You are an SEO expert. Generate a meta title and description.",
"excerpt": "Summarize the content in 1-2 sentences.",
"translate": "Translate the content professionally."
}
}'

Request Body

FieldTypeRequiredConstraintsDescription
provider_namestringYes1 -- 100 charsDisplay name (e.g. "OpenAI", "Claude")
base_urlstringYes1 -- 500 charsProvider API endpoint
api_keystringNomax 500 charsProvider secret key. Omit for local providers.
modelstringYes1 -- 200 charsModel identifier (e.g. "gpt-4o-mini")
temperaturenumberNo0.0 -- 2.0Sampling temperature. Default: 0.7
max_tokensintegerNo>= 1Maximum output tokens. Default: 1024
system_promptsobjectNoCustom prompts keyed by action (seo, excerpt, translate, draft_outline, draft_post). Empty uses built-in defaults.

Response -- 200 OK with the saved configuration (API key masked).

Logs an audit event: Update on ai_config.

Delete AI Configuration

Removes the AI configuration and encrypted API key for a site.

curl -X DELETE https://your-site.com/api/v1/sites/{site_id}/ai/config \
-H "X-API-Key: your_api_key"

Response -- 204 No Content

Test Provider Connection

Sends a minimal prompt to the configured provider to verify connectivity and API key validity. Always returns 200 with a success flag -- never fails with a provider error status.

curl -X POST https://your-site.com/api/v1/sites/{site_id}/ai/test \
-H "X-API-Key: your_api_key"

Response -- 200 OK

{
"success": true,
"message": "Connection successful — AI provider responded correctly."
}

On failure:

{
"success": false,
"message": "Connection failed: 401 Unauthorized"
}

Returns 404 if no AI configuration exists for the site.

Generate Content

Generates AI content based on the specified action. Requires the AI module to be enabled for the site.

curl -X POST https://your-site.com/api/v1/sites/{site_id}/ai/generate \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"action": "seo",
"content": "Your blog post content here..."
}'

Request Body

FieldTypeRequiredConstraintsDescription
actionstringYesSee table belowThe type of content to generate
contentstringYes1 -- 50,000 charsInput content for generation
target_localestringNoTarget language code. Required when action is translate.

Actions

ActionInputOutput Fields
seoBlog body contentmeta_title, meta_description
excerptBlog body contentexcerpt
translateJSON with translatable fields + target_localetitle, subtitle, excerpt, body, meta_title, meta_description
draft_outlineTopic idea or descriptiontitle, subtitle, outline
draft_postTitle + outline textbody, excerpt, meta_title, meta_description

Response -- 200 OK

{
"meta_title": "How to Build a Headless CMS",
"meta_description": "Learn how to build a fast, modern headless CMS with Rust and React.",
"excerpt": "A practical guide to building a headless CMS from scratch.",
"title": null,
"subtitle": null,
"body": null,
"outline": null
}

Fields not applicable to the requested action are returned as null.

Errors

StatusCondition
400AI not configured, invalid action, missing target_locale for translate
403AI module disabled for the site
429Rate limited by provider

Logs an audit event: Create on ai_generation with the action type.

Translation Input Format

The translate action expects content to be a JSON string containing the fields to translate:

curl -X POST https://your-site.com/api/v1/sites/{site_id}/ai/generate \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"action": "translate",
"content": "{\"title\": \"My Post\", \"body\": \"Full content here...\"}",
"target_locale": "de"
}'

Each field is translated individually with constraints appropriate to its type (e.g. meta titles respect length limits). Markdown formatting is preserved in the body field.

List Available Models

Queries the provider's API to discover which models are available. Useful for populating a model selector in the UI.

curl -X POST https://your-site.com/api/v1/sites/{site_id}/ai/models \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"base_url": "https://api.openai.com",
"api_key": "sk-...",
"provider_name": "OpenAI"
}'

Request Body

FieldTypeRequiredConstraintsDescription
base_urlstringYes1 -- 500 charsProvider API endpoint
api_keystringNomax 500 charsProvider secret key
provider_namestringYes1 -- 100 charsProvider name for detection logic

Response -- 200 OK

{
"models": [
"gpt-4o",
"gpt-4o-mini",
"gpt-4-turbo",
"gpt-3.5-turbo"
]
}
info

For Anthropic, model listing returns a static list of known Claude models rather than querying an API. For Ollama, Forja tries the /api/tags endpoint first, then falls back to /v1/models.

Provider Detection

Forja auto-detects the provider type from the URL and name to choose the correct API format:

ConditionDetected AsBehavior
URL contains anthropic.com or name contains "claude" / "anthropic"AnthropicUses Messages API format
URL port :11434 or name contains "ollama"OllamaSpecial model listing
URL is localhost / 127.0.0.1 / 0.0.0.0Local providerDisables JSON mode, uses XML output
Everything elseOpenAI-compatibleStandard Chat Completions API