Content Types
Templates that control the format, structure, and rules for each kind of generated content.
π‘ Building a query-only agent? Skip this page. Content types are only needed for content generation.
Content types are how an agent controls what kind of output the content engine produces. A content type defines the format (blog post, FAQ, social post), the structure (sections, length, CTAs), and any format-specific writing rules. Without a content type, there's no generation β every generation call requires one.
---
When to use content types
An agent should interact with content types in these situations:
content_type_id. The agent must either select an existing content type or create one.template field needs adjusting.---
List before you create
Before creating a new content type, always check what already exists:
import os, requests
KEY = os.environ["SENSO_API_KEY"]
BASE = "https://apiv2.senso.ai/api/v1"
HEADERS = {"X-API-Key": KEY, "Content-Type": "application/json"}
resp = requests.get(f"{BASE}/org/content-types", headers=HEADERS)
data = resp.json()
for ct in data["content_types"]:
print(f" {ct['content_type_id']}: {ct['name']}")curl https://apiv2.senso.ai/api/v1/org/content-types \
-H "X-API-Key: $SENSO_API_KEY"This avoids duplicates and helps the agent select the right template for the user's request. Only create a new content type when nothing existing fits.
Matching user intent to content types
When a user asks an agent to generate content, the agent should reason about which content type fits:
| User says | Look for content type named |
|---|---|
| "Write a blog post about X" | "Blog Post", "Article" |
| "Create an FAQ for X" | "FAQ Article", "FAQ" |
| "Draft a social post about X" | "LinkedIn Post", "Social Media", "Twitter Post" |
| "Compare X and Y" | "Product Comparison", "Comparison Page" |
| "Write a help article for X" | "Help Article", "Knowledge Base Article" |
---
Creating a content type
resp = requests.post(f"{BASE}/org/content-types", headers=HEADERS, json={
"name": "FAQ Article",
"config": {
"template": "A concise FAQ article under 800 words. Structure: one clear question as the title, a direct answer in the first paragraph, then supporting detail. End with a related-questions section.",
"cta_text": "Still have questions? Contact our support team",
"cta_destination": "https://acmecu.com/support",
"writing_rules": [
"Use active voice",
"Include at least one concrete example",
"Link to related articles where relevant",
],
}
})
content_type = resp.json()
print(f"Created: {content_type['content_type_id']}")curl -X POST https://apiv2.senso.ai/api/v1/org/content-types \
-H "X-API-Key: $SENSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "FAQ Article",
"config": {
"template": "A concise FAQ article under 800 words. Structure: one clear question as the title, a direct answer in the first paragraph, then supporting detail. End with a related-questions section.",
"cta_text": "Still have questions? Contact our support team",
"cta_destination": "https://acmecu.com/support",
"writing_rules": [
"Use active voice",
"Include at least one concrete example",
"Link to related articles where relevant"
]
}
}'What each config field controls
| Field | Effect on generated content |
|---|---|
template | The most important field. Describes the format, structure, length, and style. The engine follows this as its primary instruction for shaping output. |
cta_text | Call-to-action text appended to the end of generated content. Omit if the format doesn't need a CTA. |
cta_destination | URL the CTA links to. Only used if cta_text is set. |
writing_rules | Format-specific rules that layer on top of the brand kit's global_writing_rules. |
Writing a good template
The template field is the generation instruction. Be explicit about structure and constraints:
Vague β produces inconsistent output:
"A blog post about the topic"
Specific β produces predictable, well-structured output:
"A 1000-1500 word blog post. Start with a hook that poses a question. Use H2 subheadings every 2-3 paragraphs. Include a key takeaway callout box after the introduction. End with a 2-3 sentence summary and a single CTA."
---
How rules layer
During generation, the engine combines rules from two sources:
Brand Kit Content Type
βββ global_writing_rules βββ writing_rules
β "Use active voice" β "Include a comparison table"
β "Keep sentences under 25 words"β "Be balanced about competitors"
β β
βββββββββββββ¬βββββββββββββββββββββββ
βΌ
Both sets active during generation
Use global rules for org-wide standards that apply to everything β voice, sentence length, acronym policy.
Use content-type rules for format-specific guidance β "include a comparison table" only makes sense for comparison pages, not FAQs.
An agent should avoid duplicating rules. If "Use active voice" is already in the brand kit, don't repeat it in every content type.
---
Common content type patterns
These patterns cover the most common user requests. An agent can use these as starting points and adjust based on user preferences.
FAQ article
{
"name": "FAQ Article",
"config": {
"template": "A concise FAQ article under 800 words. One clear question as the title, a direct answer in the first paragraph, then supporting detail. End with 2-3 related questions.",
"cta_text": "Contact support",
"cta_destination": "https://example.com/support",
"writing_rules": ["Include one concrete example", "Define technical terms inline"]
}
}Blog post
{
"name": "Blog Post",
"config": {
"template": "A 1000-1500 word blog post. Start with a hook, use H2 subheadings every 2-3 paragraphs, include a key takeaway callout box, and end with a summary.",
"cta_text": "Try it free for 14 days",
"cta_destination": "https://example.com/trial",
"writing_rules": ["Write at an 8th-grade reading level", "Include at least 2 real-world examples"]
}
}Social media post
{
"name": "LinkedIn Post",
"config": {
"template": "A LinkedIn post under 200 words. Lead with a surprising insight or question. Use short paragraphs (1-2 sentences). End with a question to drive engagement.",
"cta_text": "Learn more",
"cta_destination": "https://example.com/blog",
"writing_rules": ["No hashtags in the body β add 3-5 at the end", "Avoid corporate jargon"]
}
}Product comparison
{
"name": "Product Comparison",
"config": {
"template": "A structured comparison page under 1200 words. Sections: Overview, Key Differences (as a table), Pros and Cons for each option, and a Recommendation.",
"cta_text": "Start your free trial",
"cta_destination": "https://example.com/trial",
"writing_rules": ["Include a comparison table with at least 5 dimensions", "Be balanced β acknowledge competitor strengths"]
}
}---
Managing content types
There are two ways to update a content type:
PATCH β merges only the fields you provide. Safe for single-field updates.PUT β fully replaces name and config. Use this when you want to rewrite the entire definition.Partial update with PATCH
# Update just the template, leaving other config fields untouched
resp = requests.patch(f"{BASE}/org/content-types/{ct_id}", headers=HEADERS, json={
"config": {"template": "Updated template instruction"},
})curl -X PATCH https://apiv2.senso.ai/api/v1/org/content-types/{id} \
-H "X-API-Key: $SENSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"config": {"template": "Updated template instruction"}}'Full replacement with PUT
PUT requires both name and config. Use read β merge β write to avoid losing existing fields:
# Get a content type
ct = requests.get(f"{BASE}/org/content-types/{ct_id}", headers=HEADERS).json()
# Merge changes, then write the full object
ct["config"]["template"] = "Updated template instruction"
resp = requests.put(f"{BASE}/org/content-types/{ct_id}", headers=HEADERS, json={
"name": ct["name"],
"config": ct["config"],
})
# Delete a content type
requests.delete(f"{BASE}/org/content-types/{ct_id}", headers=HEADERS)# Get a content type
curl https://apiv2.senso.ai/api/v1/org/content-types/{id} \
-H "X-API-Key: $SENSO_API_KEY"
# Update (full replacement)
curl -X PUT https://apiv2.senso.ai/api/v1/org/content-types/{id} \
-H "X-API-Key: $SENSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "FAQ Article (Updated)", "config": {"template": "Updated template"}}'
# Delete
curl -X DELETE https://apiv2.senso.ai/api/v1/org/content-types/{id} \
-H "X-API-Key: $SENSO_API_KEY"---
Using content types with generation
Every generation call pairs a content type with a prompt (question):
resp = requests.post(f"{BASE}/org/content-generation/sample", headers=HEADERS, json={
"geo_question_id": prompt_id,
"content_type_id": content_type_id,
})
gen = resp.json()
print(f"Title: {gen['seo_title']}")
print(gen["raw_markdown"][:300])curl -X POST https://apiv2.senso.ai/api/v1/org/content-generation/sample \
-H "X-API-Key: $SENSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"geo_question_id": "prompt-uuid",
"content_type_id": "content-type-uuid"
}'The engine combines: 1. Brand kit β voice, persona, global rules (who you sound like) 2. Content type β template, format rules (what shape the output takes) 3. Compiled knowledge base β verified context from your ingested raw sources (what the content is about)
See Core Concepts for the full generation workflow.
---
Using the CLI
# List content types
senso content-types list --output json
# Create a content type
senso content-types create --data '{
"name": "FAQ Article",
"config": {
"template": "A concise FAQ under 800 words",
"cta_text": "Contact support",
"cta_destination": "https://acmecu.com/support",
"writing_rules": ["Use active voice"]
}
}'
# Get a specific content type
senso content-types get <content-type-id>
# Full replacement (PUT) β both name and config required
senso content-types update <content-type-id> --data '{
"name": "Updated FAQ",
"config": { "template": "Updated template" }
}'
# Partial update (PATCH) β only the fields you send are changed
senso content-types patch <content-type-id> --data '{
"config": { "template": "Updated template instruction" }
}'
# Delete a content type
senso content-types delete <content-type-id>---
Errors
| Status | Meaning |
|---|---|
400 | Validation error β name and config are required |
401 | Missing or invalid API key |
404 | Content type not found |
409 | A content type with this name already exists |
