Configuration API
Set up your brand, products, personas, and publish targets. Configuration is done once and updated as needed.
Authentication
All endpoints require a valid API key in the Authorization header:
Authorization: Bearer sk_live_...
The API key determines which company is being configured. Generate keys in Settings > API Keys.
GET /api/v1/config
Returns the full current configuration for the company associated with the API key — brand settings, products (with themes, competitors, keywords), and personas (with product contexts).
Request
curl -H "Authorization: Bearer sk_live_..." \
https://your-domain.com/api/v1/config
Response 200 OK
{
"company": {
"id": "abc123",
"name": "LexiRune",
"slug": "lexirune",
"website": "https://lexirune.com",
"industry": "Brand naming intelligence platform",
"brandVoice": "LexiRune writes with editorial precision...",
"rssFeedUrl": "",
"brandImageStyle": null
},
"products": [
{
"id": "prod_abc",
"name": "LexiRune Free Scorer",
"tagline": "What does your name actually score?",
"description": "...",
"targetAudience": [],
"keywords": ["brand name score", "phonetic name analysis"],
"ctaText": "Score your name free →",
"ctaUrl": "https://lexirune.com",
"competitors": [
{ "name": "NameScore", "url": "https://namescore.io", "blogUrl": "", "deepAnalysisEnabled": true }
],
"themes": [
{
"id": "theme_1",
"name": "Brand Name Analysis",
"description": "Score well-known brand names...",
"seedKeywords": ["why does Slack sound good"],
"weight": 35,
"postsPublished": 0,
"status": "active"
}
]
}
],
"personas": [
{
"id": "pers_abc",
"name": "Alex",
"role": "First-time Founder — Pre-launch",
"preferredTone": "Direct and empathetic...",
"language": "English",
"productContexts": [
{
"productId": "prod_abc",
"painPoints": ["Has a name but cannot articulate why it feels off"],
"goals": ["Understand why the current name feels wrong"]
}
]
}
]
}
POST /api/v1/config
Import configuration — company settings, products, and personas in a single request. Accepts the same JSON format as the UI JSON export.
Merge behavior (default)
By default, the API uses a non-destructive merge:
- Company scalar fields (website, industry, rssFeedUrl): only set if currently empty.
- Brand voice & image kit: always overwritten (these are meant to be the latest version).
- Products: matched by name (case-insensitive). New themes, keywords, competitors, and target audience entries are appended. Scalar fields (tagline, description, ctaText, ctaUrl) are only set if currently empty.
- Personas: matched by name + role. New product contexts are appended. Scalar fields (preferredTone, language) are only set if currently empty.
Force update
Pass "force": true to overwrite all fields on existing records instead of merging. New items are always created regardless of this flag.
Request
curl -X POST \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d @config.json \
https://your-domain.com/api/v1/config
{
"force": false,
"company": {
"website": "https://lexirune.com",
"industry": "Brand naming intelligence platform",
"brandVoice": "LexiRune writes with editorial precision...",
"rssFeedUrl": "",
"brandImageStyle": {
"colors": ["#2563eb"],
"styleAdjectives": ["minimalist"],
"mood": "professional",
"elementsToAvoid": ["faces"],
"promptSuffix": ""
}
},
"products": [
{
"name": "LexiRune Free Scorer",
"tagline": "What does your name actually score?",
"description": "...",
"keywords": ["brand name score"],
"ctaText": "Score your name free →",
"ctaUrl": "https://lexirune.com",
"competitors": [
{ "name": "NameScore", "url": "https://namescore.io" }
],
"themes": [
{
"name": "Brand Name Analysis",
"description": "Score well-known brand names...",
"seedKeywords": ["why does Slack sound good"],
"weight": 35,
"status": "active"
}
]
}
],
"personas": [
{
"name": "Alex",
"role": "First-time Founder — Pre-launch",
"preferredTone": "Direct and empathetic...",
"language": "English",
"productContexts": [
{
"_originalProductId": "old_product_id",
"painPoints": ["Has a name but cannot articulate why it feels off"],
"goals": ["Understand why the current name feels wrong"]
}
]
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
force |
boolean | No | true = overwrite all fields. Default: false (merge only). |
company |
object | No | Company-level settings to update |
company.brandVoice |
string | No | Brand voice guidelines |
company.brandImageStyle |
object | No | Brand image kit |
company.website |
string | No | Company website URL |
company.industry |
string | No | Industry description |
company.rssFeedUrl |
string | No | RSS feed URL for post syncing |
products |
array | No | Products to create or update (matched by name) |
personas |
array | No | Personas to create or update (matched by name + role) |
Response 200 OK
{
"company_updated": true,
"company_fields_changed": ["brandVoice", "website", "industry"],
"products_created": 1,
"products_updated": 2,
"personas_created": 0,
"personas_updated": 3
}
Product-persona linking
Persona productContexts reference products by ID. When importing, use _originalProductId in each context — the API maps old IDs to the resolved (new or existing) product IDs automatically. Alternatively, pass productId directly if you already know the Firestore product ID.
POST /v1/config/brand (planned)
Configure brand identity — name, voice, industry, and image style.
Request
{
"name": "Acme Healthcare",
"industry": "Healthcare Technology",
"website": "https://acmehealthcare.com",
"brand_voice": "Professional, data-driven, empathetic. Never use first-person.",
"image_style": {
"colors": ["#2563eb", "#06b6d4"],
"adjectives": ["minimalist", "medical", "technological"],
"mood": "professional, clean, modern",
"avoid": ["blood", "violence", "real faces"]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Brand name |
industry |
string | Yes | Industry vertical |
website |
string | No | Brand website URL |
brand_voice |
string | Yes | Tone and style guidelines |
image_style |
object | No | Featured image generation style |
image_style.colors |
string[] | No | Brand colors (hex) |
image_style.adjectives |
string[] | No | Visual style keywords |
image_style.mood |
string | No | Overall visual mood |
image_style.avoid |
string[] | No | Elements to avoid in images |
Response 200 OK
{
"brand_id": "brd_xyz",
"status": "configured"
}
POST /v1/config/products (planned)
Add products with competitors, keywords, and themes for targeted content.
Request
{
"brand_id": "brd_xyz",
"products": [
{
"name": "WardControl",
"tagline": "AI hospital bed management",
"description": "Real-time bed management and patient flow optimization...",
"keywords": ["bed management", "patient flow"],
"cta_text": "Book a demo",
"cta_url": "https://wardcontrol.com/demo",
"competitors": [
{ "name": "HospitalFlowPro", "url": "https://hospitalflowpro.com" }
],
"themes": ["bed optimization", "patient flow", "capacity planning"]
}
]
}
Response 200 OK
{
"products_configured": 1,
"product_ids": ["prod_abc"]
}
POST /v1/config/personas (planned)
Define target audiences with product-specific pain points and goals.
Request
{
"brand_id": "brd_xyz",
"personas": [
{
"name": "Hospital Director",
"role": "C-level Decision Maker",
"tone": "Professional, data-driven",
"language": "en",
"product_contexts": [
{
"product_id": "prod_abc",
"pain_points": ["No real-time bed visibility", "Long ER wait times"],
"goals": ["Reduce wait times by 30%", "Bed occupancy above 90%"]
}
]
}
]
}
Response 200 OK
{
"personas_configured": 1
}
POST /v1/config/publish-targets (planned)
Set up where blogAIzer delivers finished content.
Request
{
"brand_id": "brd_xyz",
"targets": [
{
"type": "wordpress",
"name": "Main Blog",
"url": "https://acmehealthcare.com",
"username": "api-user",
"app_password": "xxxx xxxx xxxx xxxx",
"seo_plugin": "rankmath"
},
{
"type": "webhook",
"name": "My Bot",
"url": "https://mybot.com/webhook/blogaizer",
"auth_token": "Bearer my_secret"
}
]
}
| Target Type | Description |
|---|---|
wordpress |
Direct publish via WordPress REST API + Rank Math or Yoast SEO |
webhook |
POST article JSON to your URL |
nextjs_api |
Publish via Next.js API route |
Response 200 OK
{
"targets_configured": 2
}
