Skip to content

Agent Core

The Agent Core API exposes the agent setup canvas (gen.pro/{agent_id}/setup) as a flat, PAT-authenticated API. One GET returns every field; one PATCH updates any subset. Field names mirror exactly what the Setup canvas FE saves, so developers and the FE speak the same language.

Use this when you want to configure an agent programmatically. It replaces having to call multiple endpoints separately.

A single PATCH may update multiple internal systems at once. You see one call and get 200 if all succeed, or 207 Multi-Status with per-section status if any one section fails.

linked_accounts vs monitored vs research_topics — these are three different concepts.

  • linked_accounts = the agent’s own brand social accounts (the TikTok/Instagram/website the agent posts from).
  • monitored = inspiration sources — other creators, hashtags, or search keywords the agent watches for trending content.
  • research_topics = expertise areas (labeled “Expertise” in the UI) — subjects the agent needs to stay current on. The platform automatically researches each topic daily and feeds the results into content generation. Only include topics where recent events matter — leave empty for evergreen niches.

Don’t mix them. Putting a competitor’s TikTok URL in linked_accounts will eventually appear on the agent’s own “My Socials” list and confuse the content pipeline.

description vs personality — these are different lengths.

  • description is a 2-3 sentence brand summary. Hard-capped at 500 characters.
  • personality is the full persona text — voice, backstory, behavior rules. Max 20000 characters.

If you try to put a 3000-character persona into description, the API returns a 422. This is intentional.

GET /v1/agents/{agent_id}/core

Returns every field as a flat object. Fields that haven’t been set return null or empty arrays.

Response 200:

{
"agent_id": "abc123",
"brand_name": "Santiago",
"description": "Santiago is a San Antonio street food scout who hunts the best tacos in the city.",
"identity_type": "character",
"goal": "growth",
"target_platforms": ["tiktok", "instagram"],
"shortform": true,
"longform": false,
"onboarding_status": "profile_complete",
"keywords": ["streetfood", "tacotok", "sanantonioeats", "foodtruckfinds"],
"monitored": [
{ "handle": "https://tiktok.com/@keilapacheco", "item_type": "account" },
{ "handle": "tacotok", "item_type": "hashtag" }
],
"research_topics": [
{ "topic": "new food truck openings in San Antonio" },
{ "topic": "viral street food TikTok moments" },
{ "topic": "Texas food festival announcements" }
],
"linked_accounts": [
{ "id": 42, "url": "https://tiktok.com/@santiago_real", "platform": "tiktok", "display_name": "Santiago 🌮" }
],
"personality": "Santiago grew up eating at his tía's taco stand in south San Antonio...",
"default_user_voice": {
"voice_id": "21m00Tcm4TlvDq8ikWAM",
"name": "Rachel",
"source": "public"
}
}
PATCH /v1/agents/{agent_id}/core

Flat merge-patch body. Send only the fields you want to change — omitted fields are left unchanged.

FieldSemantics
Scalars (brand_name, description, identity_type, goal, shortform, longform, onboarding_status, personality)Replace
keywordsReplace the full list
target_platformsReplace the full list
monitoredReplace the full list — items you don’t send are deleted
research_topicsReplace the full list — topics you don’t send are deleted. Max 20 items.
linked_accountsReplace the full list — rows you don’t send are deleted. Rows with id are updated in place; rows without id are inserted.
default_user_voiceReplace

To add a single monitored item or linked_account without touching others, GET /core first, append, then PATCH with the full list.

  • Unknown fields return 422 (extra="forbid" on every model).
  • description max 500 chars.
  • personality max 20000 chars.
  • identity_type strict enum: "brand" | "character".
  • monitored[].item_type strict enum: "account" | "hashtag" | "keyword".
  • research_topics max 20 items, each topic max 500 chars.
  • linked_accounts[].url required.
  • monitored[], research_topics[], and linked_accounts[] also reject unknown keys.

If you send keywords without also sending monitored, the writer automatically populates monitored with [{handle: kw, item_type: "keyword"}] for each keyword, so the nightly monitoring cron stays in sync. This matches what the brand-analysis onboarding task does. If you send both, your explicit monitored wins — the auto-mirror is skipped.

200 on full success, 207 Multi-Status on partial failure. The body is a map keyed by upstream target, not by request field:

{
"brand": { "status": "ok", "data": { "brand_name": "Santiago", "keywords": ["streetfood"], ... } },
"personality": { "status": "ok", "data": "Santiago grew up eating at..." },
"voice": { "status": "error", "error": "voice service unavailable" }
}

Sections only appear if the PATCH touched at least one field they own. A PATCH that only sets brand_name returns { "brand": { "status": "ok", ... } } — no personality or voice entry.

Example — set up a character in one call

Section titled “Example — set up a character in one call”
Terminal window
curl -X PATCH https://api.gen.pro/v1/agents/abc123/core \
-H "X-API-Key: $GEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"brand_name": "Santiago",
"description": "Santiago is a San Antonio street food scout who hunts the best tacos in the city.",
"identity_type": "character",
"goal": "growth",
"target_platforms": ["tiktok", "instagram"],
"shortform": true,
"longform": false,
"onboarding_status": "profile_complete",
"keywords": ["streetfood", "tacotok", "sanantonioeats"],
"monitored": [
{ "handle": "https://tiktok.com/@keilapacheco", "item_type": "account" },
{ "handle": "tacotok", "item_type": "hashtag" }
],
"research_topics": [
{ "topic": "new food truck openings in San Antonio" },
{ "topic": "viral street food TikTok moments" }
],
"linked_accounts": [
{ "url": "https://tiktok.com/@santiago_real", "platform": "tiktok", "display_name": "Santiago" }
],
"personality": "Santiago grew up eating at his tía'\''s taco stand in south San Antonio. He'\''s 28, easy-going, trusts his tongue over reviews, and will fight anyone who says Austin has better tacos.",
"default_user_voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "source": "public" }
}'

TypeScript SDK equivalent:

import { GenClient } from '@poweredbygen/gen-sdk';
const gen = new GenClient({ apiKey: process.env.GEN_API_KEY! });
const result = await gen.agents.updateCore('abc123', {
brand_name: 'Santiago',
description: 'San Antonio street food scout who hunts the best tacos.',
identity_type: 'character',
goal: 'growth',
target_platforms: ['tiktok', 'instagram'],
shortform: true,
keywords: ['streetfood', 'tacotok', 'sanantonioeats'],
monitored: [
{ handle: 'https://tiktok.com/@keilapacheco', item_type: 'account' },
],
research_topics: [
{ topic: 'new food truck openings in San Antonio' },
{ topic: 'viral street food TikTok moments' },
],
linked_accounts: [
{ url: 'https://tiktok.com/@santiago_real', platform: 'tiktok' },
],
personality: 'Santiago grew up eating at his tía\'s taco stand...',
default_user_voice: { voice_id: '21m00Tcm4TlvDq8ikWAM', source: 'public' },
});
if (result.personality?.status === 'error') {
console.error('personality write failed:', result.personality.error);
}
CodeMeaning
401Missing or invalid X-API-Key
404Agent not found
422Validation error — unknown field, bad enum, length cap exceeded, or missing required sub-field like monitored[].item_type
207Partial success. Inspect per-target status in the response body.
  • Voice API — voice library, design, cloning, preview (separate endpoint tree)
  • Discovery API — listing agents, workspaces, organizations