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