diff --git a/public/llms-full.txt b/public/llms-full.txt index fa91ab6..422f1bf 100644 --- a/public/llms-full.txt +++ b/public/llms-full.txt @@ -446,53 +446,84 @@ Poll: GET /v1/user_jobs/{id}?agent_id={id} Prerequisites: Agent must have connected TikTok account (via AURA). Media must be publicly accessible. -## Agent Core API (GEN-2755) +## Agent Core API -**PREFERRED way to configure an agent.** Single-endpoint read/write for the entire agent setup canvas: identity, overview (brand profile), personality, inspiration sources, voice, look (+ reference images), and the agent's own social accounts. Replaces the legacy per-resource TrendPulse calls when you're setting up a new agent or editing its configuration. +**PREFERRED way to configure an agent.** Flat read/write for the entire agent setup canvas in a single call. Every field name mirrors exactly what the GEN Setup canvas saves, so developers and the app speak the same language. -**Backing service:** `agent-core` on EC2 (ports 8800 prod, 8801 staging). Exposed through `api.gen.pro` (CloudFront) and `staging-api.gen.pro` for PAT callers, and through `trendpulse.gen.pro` / `staging-trendpulse.gen.pro` for JWT callers. All routes PAT-authenticated via `X-API-Key`. +**Base URL:** `https://api.gen.pro/v1`. All routes authenticated via `X-API-Key` header with a Personal Access Token. + +### Fields + +A single PATCH may touch multiple internal systems. You see one call and get `200` if all succeed, or `207 Multi-Status` with per-section status if any one section fails. + +### Two critical distinctions + +- **`linked_accounts` vs `monitored` vs `research_topics`.** `linked_accounts` = the agent's OWN brand socials (the TikTok/Instagram/website the agent posts from). `monitored` = inspiration sources the agent watches for trending content (other creators, hashtags, or search keywords). `research_topics` = expertise areas the agent stays current on — the platform researches each daily and feeds results into content ideas (labeled "Expertise" in the UI; only include topics where recent events matter). Not the same thing — putting a competitor's URL into `linked_accounts` will eventually show up on the agent's own "My Socials" list. +- **`description` vs `personality`.** `description` is a short 2-3 sentence brand summary, hard-capped at 500 chars. `personality` is the full persona text, max 20000 chars. A 3000-char persona dumped into `description` returns 422 by design. ### GET /v1/agents/{agent_id}/core -Returns every section of the agent setup canvas in one call. +Returns every field as a flat object. Response: ```json { - "identity": { "name": "Santiago", "profile_photo_url": "https://cdn.example/a.png" }, - "overview": { - "agent_id": "abc123", - "brand_name": "Santiago", - "description": "Santiago is a San Antonio street food scout...", - "identity_type": "character", - "goal": "growth", - "keywords": ["streetfood", "foodtruck", "tacotok"], - "target_platforms": ["tiktok", "instagram"], - "shortform": true, - "longform": false, - "primary_format": null, - "onboarding_status": "active" - }, - "personality": "Santiago grew up eating at his tia's taco stand...", - "inspiration": [{ "id": 1, "url": "https://tiktok.com/@keilapacheco", "platform": "tiktok" }], - "voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "name": "Rachel", "source": "public" }, - "look": { - "description": "Latino male early 30s, athletic build", - "reference_images": [{ "id": 11, "url": "https://cdn.example/ref1.png" }] - }, - "accounts": [{ "id": 42, "url": "https://tiktok.com/@santiago_real", "platform": "tiktok" }] + "agent_id": "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, + "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" }, + { "topic": "Texas food festival announcements" } + ], + "linked_accounts": [ + { "id": 42, "url": "https://tiktok.com/@santiago_real", "platform": "tiktok", "display_name": "Santiago" } + ], + "look": "Warm earthy tones, close-up street food shots, natural light. Mid-20s Latino man, often seen on bustling sidewalks with a taco in hand.", + "personality": "Santiago grew up eating at his tia's taco stand in south San Antonio...", + "default_user_voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "name": "Rachel", "source": "public" } } ``` ### PATCH /v1/agents/{agent_id}/core -Write any combination of sections in a single call. Merge semantics for `identity`, `overview`, `look.description`. Replace semantics for `personality`, `inspiration`, `voice`, `accounts`. +Flat merge-patch. Send only the fields you want to change. Unknown fields return 422. + +**Field-level semantics:** +- Scalars replace: `brand_name`, `description`, `identity_type`, `goal`, `shortform`, `longform`, `onboarding_status`, `look`, `personality` +- List fields use FULL replacement (missing = deleted): `keywords`, `target_platforms`, `monitored`, `research_topics`, `linked_accounts` +- `linked_accounts` diffing: rows with `id` are updated in place, rows without `id` are inserted, rows absent from the list are deleted. To add one row without touching others, GET first, append, PATCH. +- `default_user_voice` replaces + +**Keyword auto-mirror:** Sending `keywords` without `monitored` auto-populates `monitored` with `[{handle: kw, item_type: "keyword"}]` so the nightly monitoring cron stays in sync. Send both explicitly if you want different values. -Returns `200 OK` on full success. On partial failure, returns `207 Multi-Status` with per-section results: +**Validation:** +- `description` max 500 chars +- `look` max 2000 chars +- `personality` max 20000 chars +- `identity_type` enum: `"brand"` | `"character"` +- `monitored[].item_type` enum: `"account"` | `"hashtag"` | `"keyword"` +- `research_topics` max 20 items, each `topic` max 500 chars +- `linked_accounts[].url` required +- All nested objects reject unknown keys + +Returns `200 OK` on full success. On partial failure, returns `207 Multi-Status` with per-**section** results (not per-field). Sections only appear if the PATCH touched at least one field they own: ```json { - "identity": { "status": "ok", "data": { ... } }, - "overview": { "status": "error", "error": "..." } + "brand": { "status": "ok", "data": { "brand_name": "Santiago", ... } }, + "personality": { "status": "ok", "data": "Santiago grew up..." }, + "voice": { "status": "error", "error": "voice service unavailable" } } ``` @@ -502,34 +533,30 @@ curl -X PATCH https://api.gen.pro/v1/agents/abc123/core \ -H "X-API-Key: $GEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ - "identity": { "name": "Santiago" }, - "overview": { - "brand_name": "Santiago", - "description": "Santiago is a San Antonio street food scout...", - "identity_type": "character", - "goal": "growth", - "keywords": ["streetfood", "tacotok"], - "target_platforms": ["tiktok", "instagram"], - "shortform": true - }, - "personality": "Santiago grew up eating at his tia\u0027s taco stand...", - "inspiration": [{ "url": "https://tiktok.com/@keilapacheco", "platform": "tiktok" }], - "look": { "description": "Latino male early 30s, athletic build" }, - "voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "source": "public" }, - "accounts": [{ "url": "https://tiktok.com/@santiago_real", "platform": "tiktok" }] + "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, + "longform": false, + "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", "display_name": "Santiago" } + ], + "look": "Warm earthy tones, close-up street food shots, natural light. Mid-20s Latino man, often seen on bustling sidewalks with a taco in hand.", + "personality": "Santiago grew up eating at his tia\u0027s taco stand in south San Antonio. 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" } }' ``` -### Item-level endpoints (append single items without replacing the list) - -- POST /v1/agents/{agent_id}/core/identity/profile-photo — multipart `file=@photo.jpg` -- POST /v1/agents/{agent_id}/core/inspiration — body `{url, platform?}` -- DELETE /v1/agents/{agent_id}/core/inspiration/{item_id} -- POST /v1/agents/{agent_id}/core/accounts — body `{url, platform?, display_name?}` -- DELETE /v1/agents/{agent_id}/core/accounts/{account_id} -- POST /v1/agents/{agent_id}/core/look/reference-images — body `{url}` -- DELETE /v1/agents/{agent_id}/core/look/reference-images/{image_id} - ## Voice API (Agent Core) All under `/v1/agents/{agent_id}/voice/*`. PAT auth. @@ -697,9 +724,9 @@ Each idea includes: - rationale — why this idea works, with specific data points ### Asset Sources -- Social content (TikTok/IG/YouTube): assets.gen.buzz (permanent S3 URLs) -- Web research assets: gen-agentic-assets S3 bucket -- User uploads: Rails ContentResource +- Social content (TikTok/IG/YouTube): permanent CDN URLs +- Web research assets: sourced by the research agent +- User uploads: via the Content Resources API ### Research-Backed Generation The agent automatically: diff --git a/public/llms.txt b/public/llms.txt index 9430df2..88ab76d 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -2,7 +2,7 @@ > GEN is an Autonomous Social Media Agent platform. It detects trends, generates video content, publishes across platforms, and improves automatically. -Two surfaces: **Agent Core** (`api.gen.pro/v1/agents/{id}/core` + `/voice`) for setting up an agent's identity, personality, voice, and brand; and **Auto Content Engine** (`api.gen.pro/v1/autocontentengine/*`) for producing videos from those agents. Both authenticate with an X-API-Key PAT. +Two surfaces: **Agent Core** (`api.gen.pro/v1/agents/{id}/core` + `/voice`) for setting up an agent's brand, personality, inspiration sources, and voice; and **Auto Content Engine** (`api.gen.pro/v1/autocontentengine/*`) for producing videos from those agents. Both authenticate with an X-API-Key PAT. For any new agent, **start with Agent Core**. Generating content before the agent is configured gives you generic results; configuring it first means every downstream feature (content ideas, template fills, voice) speaks in the agent's voice from the first call. @@ -33,29 +33,42 @@ The GEN workflow has two halves — configure the agent, then let it generate co ### Step 1: Configure the Agent Core (most important step for any new agent) -One PATCH writes identity, overview (brand profile), personality, inspiration, voice, look, and accounts. This is what every downstream feature (content ideas, template fills, voice generation) reads from. A blank agent produces generic ideas. A richly configured agent produces specific, on-brand ones. +One flat PATCH writes every field the GEN Setup canvas can save: brand, inspiration sources, personality, voice. Every field name maps 1:1 to how the FE saves it. This is what every downstream feature (content ideas, template fills, voice generation) reads from. A blank agent produces generic ideas. A richly configured agent produces specific, on-brand ones. PATCH /v1/agents/{agent_id}/core ```json { - "identity": { "name": "Santiago" }, - "overview": { - "brand_name": "Santiago", - "description": "Santiago is a San Antonio street food scout...", - "identity_type": "character", - "goal": "growth", - "keywords": ["streetfood", "foodtruck", "tacotok"], - "target_platforms": ["tiktok", "instagram"], - "shortform": true - }, - "personality": "Santiago grew up eating at his tias taco stand...", - "inspiration": [{ "url": "https://tiktok.com/@keithlee", "platform": "tiktok" }], - "look": { "description": "Latino male early 30s, athletic build" }, - "voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "source": "public" }, - "accounts": [{ "url": "https://tiktok.com/@santiago_sa", "platform": "tiktok" }] + "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, + "longform": false, + "keywords": ["streetfood", "foodtruck", "tacotok"], + "monitored": [ + { "handle": "https://tiktok.com/@keithlee", "item_type": "account" }, + { "handle": "sanantonioeats", "item_type": "hashtag" } + ], + "linked_accounts": [ + { "url": "https://tiktok.com/@santiago_sa", "platform": "tiktok" } + ], + "personality": "Santiago grew up eating at his tia's taco stand in south San Antonio. 28, easy-going, trusts his tongue over reviews.", + "look": "Warm earthy tones, close-up food shots, natural light", + "content_themes": ["street food", "local spots", "food reviews"], + "persona_inspirations": ["@keilapacheco", "@thetacochronicles"], + "timezone": "America/Chicago", + "default_user_voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "source": "public" } } ``` -Returns 200 on full success or 207 Multi-Status with per-section results. +Returns 200 with the full updated agent core object. Upstream Rails failures (timezone, voice, ElevenLabs key) are logged but don't affect the HTTP status — local fields save first. + +**Field distinctions:** +- `linked_accounts` = the agent's OWN brand socials (what it posts from). `monitored` = inspiration sources (creators/hashtags to watch). +- `description` is a short 2-3 sentence summary (max 500 chars); full persona text goes in `personality` (max 20000 chars). +- `content_themes` = content pillars (e.g. "street food", "travel"). `persona_inspirations` = creator accounts whose style to emulate. +- `look` = visual style description for reference image generation (max 2000 chars). +- `timezone` + `eleven_lab_api_key` = Rails-owned fields forwarded automatically; they appear in GET responses alongside the local brand fields. ### Step 2: Generate content ideas @@ -302,19 +315,21 @@ Column roles: ingredient (user-creatable), video, final_video, stats (system-man - POST /v1/user_jobs?agent_id={id} — publish content (body: {user_job_type: "publish_content", data: JSON string}) ### Agent Core (GEN-2755, recommended for agent setup) -One-call read/write for the entire agent setup canvas (identity, overview, personality, inspiration, voice, look, accounts). Prefer this over per-resource TrendPulse calls when configuring an agent. +Flat one-call read/write for the entire agent setup canvas. Field names mirror what the FE saves — no sections, no wrappers. Prefer this over per-resource TrendPulse calls. + +- GET /v1/agents/{agent_id}/core — read all fields in one call +- PATCH /v1/agents/{agent_id}/core — write any subset; 200 on full success, 207 Multi-Status on partial failure -- GET /v1/agents/{agent_id}/core — read all sections in one call -- PATCH /v1/agents/{agent_id}/core — write any combination of sections; 200 on full success, 207 Multi-Status on partial failure -- POST /v1/agents/{agent_id}/core/identity/profile-photo — multipart upload -- POST /v1/agents/{agent_id}/core/inspiration — append inspiration URL -- DELETE /v1/agents/{agent_id}/core/inspiration/{id} -- POST /v1/agents/{agent_id}/core/accounts — append the agent's own social URL -- DELETE /v1/agents/{agent_id}/core/accounts/{id} -- POST /v1/agents/{agent_id}/core/look/reference-images — add look reference image URL -- DELETE /v1/agents/{agent_id}/core/look/reference-images/{id} +Fields: +- brand_name, description, identity_type ("brand" | "character"), goal, target_platforms, shortform, longform, onboarding_status +- keywords (flat list of strings) +- monitored (list of {handle, item_type: "account"|"hashtag"|"keyword"}) — inspiration sources +- research_topics (list of {topic: string}, max 20 items, each max 500 chars) — subjects the agent stays current on (labeled "Expertise" in UI). Platform researches daily and feeds results into content ideas. Leave empty for evergreen niches. +- linked_accounts (list of {id?, url, platform, display_name}) — the agent's OWN brand socials +- personality (string, max 20000 chars) — full persona text +- default_user_voice ({voice_id, source}) — default TTS voice -Merge semantics: identity, overview, look.description. Replace semantics: personality, inspiration, voice, accounts. +Full-list replacement: `keywords`, `monitored`, `research_topics`, `target_platforms`, `linked_accounts`. To add one row without touching others, GET first, append, PATCH. Sending `keywords` without `monitored` auto-mirrors keywords into monitored with item_type='keyword'. `description` is hard-capped at 500 chars. Unknown fields return 422. ### Voice (Agent Core) - GET /v1/agents/{agent_id}/voice/library?source={public|user_designed|user_trained|user_elevenlabs} — merged voice list @@ -335,7 +350,7 @@ Voice delete + preview: - POST /v1/agents/{agent_id}/voice/{voice_id}/preview — enqueue TTS preview job (ASYNC, returns `{user_job_id}`) - GET /v1/agents/{agent_id}/voice/preview/{job_id} — poll preview job status -Auth: PAT (`X-API-Key`). Runs on `agent-core` service on EC2, exposed via `api.gen.pro` CloudFront + `staging-api.gen.pro` staging. +Auth: PAT (`X-API-Key`) via `api.gen.pro`. ## Agentic Chat API diff --git a/public/openapi.yaml b/public/openapi.yaml index 92ae5fe..5551df4 100644 --- a/public/openapi.yaml +++ b/public/openapi.yaml @@ -32,11 +32,9 @@ info: servers: - url: https://api.gen.pro/v1 - description: Production - - url: https://stagingapi.gen.pro/v1 - description: Staging + description: Auto Content Engine API - url: https://agent.gen.pro/v1 - description: Production (Agent Chat API) + description: Agent Chat API security: - apiKey: [] @@ -83,7 +81,7 @@ tags: description: (Legacy) Manage the agent's identity, voice, and brand profile. Prefer Agent Core for new work. - name: Agent Core description: | - **Preferred** single-endpoint read/write for the agent setup canvas. One PATCH configures identity, overview, personality, inspiration, voice, look, and accounts. Backed by the agent-core service; exposed through api.gen.pro. + **Preferred** single-endpoint read/write for the agent setup canvas. One flat PATCH updates every brand field, personality, and default voice in a single call. Field names mirror what the FE Setup canvas saves, so developers and the FE speak the same language. Backed by the agent-core service; exposed through api.gen.pro. - name: Agent Voice description: | Voice library, ElevenLabs integration, prompt-based design (4-step flow), audio-sample cloning, async TTS preview. @@ -2903,8 +2901,11 @@ paths: operationId: getAgentCore summary: Get the full agent setup description: | - Returns every section of the agent setup canvas in a single response: - identity, overview, personality, inspiration, voice, look, accounts. + Returns the agent's full setup as a flat object whose field names mirror + exactly what the GEN Setup canvas FE reads and writes. Fans out to + TrendPulse `/trendpulse/settings` (brand fields), Rails `agents` (voice + association), and Rails `project_nodes` (personality) and merges them + into one response. tags: [Agent Core] parameters: - name: agent_id @@ -2922,12 +2923,35 @@ paths: '404': { $ref: '#/components/responses/NotFound' } patch: operationId: patchAgentCore - summary: Update any combination of setup sections + summary: Update any combination of setup fields description: | - Merge semantics for `identity`, `overview`, `look.description`. Replace - semantics for `personality`, `inspiration`, `voice`, `accounts`. Returns - 200 on full success, 207 Multi-Status with per-section results on - partial failure. + Flat merge-patch. Send only the fields you want to change — missing + fields are not forwarded. Field names map 1:1 to the upstream service's + storage columns, with no wrappers or renames. + + **Storage map:** + - `brand_name`, `description`, `identity_type`, `goal`, `keywords`, + `monitored`, `target_platforms`, `shortform`, `longform`, + `onboarding_status`, `linked_accounts` → forwarded in one PATCH to + the TrendPulse settings endpoint, which owns diffing for + `linked_accounts` (delete-then-insert on the full list). + - `personality` → Rails `project_nodes` row with `node_type='personality'`. + - `default_user_voice` → Rails `agents.default_user_voice_attributes`. + + **Guardrails:** + - Unknown fields return 422 — typos fail fast. + - `description` is capped at 500 chars. Full persona text belongs in + `personality` (max 20000 chars). + - `identity_type` is a strict enum: `"brand"` or `"character"`. + - `monitored[].item_type` is a strict enum: `"account"`, `"hashtag"`, + or `"keyword"`. + + **Semantics:** `linked_accounts` and `monitored` use full-list + replacement. To add one row without touching others, read the current + list via GET first, append, then PATCH. + + Returns 200 on full success, 207 Multi-Status with per-target results + (`trendpulse`, `personality`, `voice`) on partial failure. tags: [Agent Core] parameters: - name: agent_id @@ -2942,156 +2966,23 @@ paths: $ref: '#/components/schemas/AgentCorePatch' responses: '200': - description: All sections updated successfully + description: All fields updated successfully content: application/json: schema: $ref: '#/components/schemas/AgentCorePatchResult' '207': - description: Partial success — inspect per-section status + description: Partial success — inspect per-target status content: application/json: schema: $ref: '#/components/schemas/AgentCorePatchResult' '401': { $ref: '#/components/responses/Unauthorized' } - - /agents/{agent_id}/core/identity/profile-photo: - post: - operationId: uploadAgentCoreProfilePhoto - summary: Upload agent profile photo - description: Multipart upload. Proxied to the Rails agents endpoint; returns the CDN URL. - tags: [Agent Core] - parameters: - - name: agent_id - in: path - required: true - schema: { type: string } - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - properties: - file: - type: string - format: binary - required: [file] - responses: - '200': - description: Uploaded - content: - application/json: - schema: - type: object - properties: - profile_photo_url: { type: string } - - /agents/{agent_id}/core/inspiration: - post: - operationId: addAgentInspiration - summary: Add one inspiration source - tags: [Agent Core] - parameters: - - name: agent_id - in: path - required: true - schema: { type: string } - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - url: { type: string } - platform: { type: string, enum: [tiktok, instagram, youtube, x, linkedin, pinterest, facebook, threads, website] } - required: [url] - responses: - '201': - description: Created - content: - application/json: - schema: { $ref: '#/components/schemas/InspirationItem' } - - /agents/{agent_id}/core/inspiration/{item_id}: - delete: - operationId: removeAgentInspiration - summary: Remove one inspiration source - tags: [Agent Core] - parameters: - - { name: agent_id, in: path, required: true, schema: { type: string } } - - { name: item_id, in: path, required: true, schema: { type: integer } } - responses: - '204': { description: Deleted } - '404': { $ref: '#/components/responses/NotFound' } - - /agents/{agent_id}/core/accounts: - post: - operationId: addAgentAccount - summary: Add one of the agent's own social accounts - tags: [Agent Core] - parameters: - - { name: agent_id, in: path, required: true, schema: { type: string } } - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - url: { type: string } - platform: { type: string } - display_name: { type: string } - required: [url] - responses: - '201': - description: Created + '422': + description: Validation error — unknown field, bad enum, or length cap exceeded content: application/json: - schema: { $ref: '#/components/schemas/AccountItem' } - - /agents/{agent_id}/core/accounts/{account_id}: - delete: - operationId: removeAgentAccount - summary: Remove one linked account - tags: [Agent Core] - parameters: - - { name: agent_id, in: path, required: true, schema: { type: string } } - - { name: account_id, in: path, required: true, schema: { type: integer } } - responses: - '204': { description: Deleted } - - /agents/{agent_id}/core/look/reference-images: - post: - operationId: addLookReferenceImage - summary: Add a look reference image URL - tags: [Agent Core] - parameters: - - { name: agent_id, in: path, required: true, schema: { type: string } } - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - url: { type: string } - required: [url] - responses: - '201': - description: Created - - /agents/{agent_id}/core/look/reference-images/{image_id}: - delete: - operationId: removeLookReferenceImage - summary: Remove a look reference image - tags: [Agent Core] - parameters: - - { name: agent_id, in: path, required: true, schema: { type: string } } - - { name: image_id, in: path, required: true, schema: { type: integer } } - responses: - '204': { description: Deleted } + schema: { $ref: '#/components/schemas/Error' } # ── Agent Voice (GEN-2755) ──────────────────────────────── /agents/{agent_id}/voice/library: @@ -3992,140 +3883,147 @@ components: description: Machine-readable error code. # ── Agent Core schemas (GEN-2755) ─────────────────────── + # Flat schema — every field name mirrors what the GEN Setup canvas FE + # saves. See api/reference/agent-core for the full storage map. AgentCore: type: object - properties: - identity: - type: object - properties: - name: { type: string, nullable: true } - profile_photo_url: { type: string, nullable: true } - overview: - $ref: '#/components/schemas/BrandOverview' - personality: - type: string - nullable: true - inspiration: - type: array - items: { $ref: '#/components/schemas/InspirationItem' } - voice: - type: object - nullable: true - properties: - voice_id: { type: string } - name: { type: string, nullable: true } - source: { type: string, enum: [public, user_designed, user_trained, user_elevenlabs] } - look: - type: object - properties: - description: { type: string, nullable: true } - reference_images: - type: array - items: - type: object - properties: - id: { type: integer } - url: { type: string } - accounts: - type: array - items: { $ref: '#/components/schemas/AccountItem' } - - BrandOverview: - type: object + description: Full agent setup as returned by GET /agents/{id}/core. properties: agent_id: { type: string } brand_name: { type: string, nullable: true } description: { type: string, nullable: true } identity_type: { type: string, enum: [brand, character], nullable: true } - goal: { type: string, nullable: true } - keywords: - type: array - items: { type: string } + goal: { type: string, nullable: true, description: "Comma-separated, e.g. 'growth,authority,sales'" } target_platforms: type: array items: { type: string } shortform: { type: boolean, nullable: true } longform: { type: boolean, nullable: true } - primary_format: { type: string, nullable: true } onboarding_status: { type: string, nullable: true } + keywords: + type: array + items: { type: string } + description: Flat keyword list used by the monitoring cron. Auto-mirrored into `monitored` on write. + monitored: + type: array + items: { $ref: '#/components/schemas/MonitoredItem' } + description: Inspiration sources — accounts, hashtags, or keywords the agent monitors for trending content. + linked_accounts: + type: array + items: { $ref: '#/components/schemas/LinkedAccount' } + description: The agent's OWN brand social links. NOT the same as `monitored` (which holds sources to watch). + personality: + type: string + nullable: true + description: Full persona text. Canonical source is the Rails `project_nodes` row with `node_type='personality'`. + default_user_voice: + $ref: '#/components/schemas/VoiceRef' AgentCorePatch: type: object - description: "Any combination of sections. Merge semantics for identity/overview/look.description, replace for personality/inspiration/voice/accounts." + additionalProperties: false + description: | + Flat merge-patch body. Send any subset of fields — missing fields are not forwarded. + Unknown fields return 422. `linked_accounts` and `monitored` use full-list replacement + semantics: rows not in the list are deleted. To add without touching others, GET first, + append, then PATCH. properties: - identity: - type: object - properties: - name: { type: string } - profile_photo_url: { type: string } - overview: - type: object - properties: - brand_name: { type: string } - description: { type: string } - identity_type: { type: string, enum: [brand, character] } - goal: { type: string } - keywords: { type: array, items: { type: string } } - target_platforms: { type: array, items: { type: string } } - shortform: { type: boolean } - longform: { type: boolean } - onboarding_status: { type: string } - personality: { type: string } - inspiration: + brand_name: { type: string, maxLength: 200 } + description: + type: string + maxLength: 500 + description: Short 2-3 sentence brand summary. NOT full persona — that goes in `personality`. + identity_type: { type: string, enum: [brand, character] } + goal: { type: string, maxLength: 100 } + target_platforms: type: array - items: - type: object - properties: - url: { type: string } - platform: { type: string } - required: [url] - look: - type: object - properties: - description: { type: string } - voice: - type: object - properties: - voice_id: { type: string } - source: { type: string, enum: [public, user_designed, user_trained, user_elevenlabs] } - accounts: + items: { type: string } + maxItems: 10 + shortform: { type: boolean } + longform: { type: boolean } + onboarding_status: { type: string, maxLength: 50 } + keywords: type: array - items: - type: object - properties: - url: { type: string } - platform: { type: string } - display_name: { type: string } - required: [url] + items: { type: string } + maxItems: 20 + description: Flat keyword list. If set without `monitored`, auto-mirrored into monitored with item_type='keyword'. + monitored: + type: array + items: { $ref: '#/components/schemas/MonitoredItem' } + maxItems: 50 + description: Full inspiration source list (replaces the existing list). + linked_accounts: + type: array + items: { $ref: '#/components/schemas/LinkedAccount' } + maxItems: 20 + personality: + type: string + maxLength: 20000 + description: Full persona text. Written to Rails `project_nodes` where `node_type='personality'`. + default_user_voice: + $ref: '#/components/schemas/VoiceRef' + + MonitoredItem: + type: object + additionalProperties: false + required: [handle, item_type] + properties: + handle: + type: string + minLength: 1 + maxLength: 300 + description: For accounts — a @handle or URL. For hashtags — the tag (with or without #). For keywords — the search term. + item_type: + type: string + enum: [account, hashtag, keyword] + + LinkedAccount: + type: object + additionalProperties: false + required: [url] + properties: + id: + type: integer + description: Row id if updating an existing row. Omit when adding a new account. + url: { type: string, minLength: 1, maxLength: 500 } + platform: + type: string + maxLength: 50 + description: 'tiktok | instagram | youtube | x | linkedin | pinterest | facebook | threads | website | other' + display_name: { type: string, maxLength: 200 } + + VoiceRef: + type: object + additionalProperties: false + required: [voice_id] + properties: + voice_id: { type: string, minLength: 1, maxLength: 200 } + source: + type: string + enum: [public, user_designed, user_trained, user_elevenlabs, eleven_labs] + name: { type: string, maxLength: 200 } AgentCorePatchResult: type: object - description: "Per-section result. status=ok means that section's write succeeded; status=error means it failed (see error field)." + description: | + Per-target result. agent-core fans out to up to three upstreams: + - `trendpulse` — any brand fields (brand_name, description, identity_type, goal, + target_platforms, shortform, longform, onboarding_status, keywords, monitored, + linked_accounts) + - `personality` — Rails project_nodes + - `voice` — Rails agents.default_user_voice_attributes + Each target appears only if the PATCH touched at least one field it owns. + `status=ok` means that target's write succeeded. `status=error` means it failed + (see `error` field). A 207 Multi-Status means at least one target errored. additionalProperties: type: object properties: status: { type: string, enum: [ok, error] } data: - description: "Section-specific payload on success." + description: Target-specific payload on success. error: type: string - description: "Error message on failure." - - InspirationItem: - type: object - properties: - id: { type: integer } - url: { type: string } - platform: { type: string } - - AccountItem: - type: object - properties: - id: { type: integer } - agent_id: { type: string } - url: { type: string } - platform: { type: string } - display_name: { type: string, nullable: true } + description: Error message on failure. VoiceLibraryItem: type: object diff --git a/src/content/docs/guides/authentication.mdx b/src/content/docs/guides/authentication.mdx index d773e55..c8c8d6a 100644 --- a/src/content/docs/guides/authentication.mdx +++ b/src/content/docs/guides/authentication.mdx @@ -154,7 +154,7 @@ curl https://api.gen.pro/v1/me \ -H "Authorization: Bearer " ``` -JWTs are issued by Dynamic XYZ during user login. They contain the user's wallet address and are validated against the GEN user database. +JWTs are issued during user login through the GEN web app and are validated against the GEN user database. **When to use each method:** diff --git a/src/content/docs/guides/quickstart.mdx b/src/content/docs/guides/quickstart.mdx index add8050..3c54fa9 100644 --- a/src/content/docs/guides/quickstart.mdx +++ b/src/content/docs/guides/quickstart.mdx @@ -63,7 +63,12 @@ curl -s -X PATCH "https://api.gen.pro/v1/agents/$AGENT_ID/core" \ "goal": "growth", "keywords": ["streetfood", "foodtruck", "tacotok"], "target_platforms": ["tiktok", "instagram"], - "shortform": true + "shortform": true, + "research_topics": [ + {"topic": "new food truck openings in San Antonio"}, + {"topic": "viral street food TikTok moments"}, + {"topic": "Texas food festival announcements"} + ] }, "personality": "Santiago grew up eating at his tias taco stand on the East Side. He believes the best food comes from the smallest kitchens. He is warm, specific, never condescending, always hungry.", "inspiration": [ @@ -182,7 +187,7 @@ See the [Content Resources guide](/reference/content-resources/) for downloading - + diff --git a/src/content/docs/reference/agent-core.mdx b/src/content/docs/reference/agent-core.mdx index 1d484b6..c52b9f9 100644 --- a/src/content/docs/reference/agent-core.mdx +++ b/src/content/docs/reference/agent-core.mdx @@ -1,167 +1,218 @@ --- title: Agent Core -description: Read and write the entire agent setup canvas — identity, overview, personality, inspiration, voice, look, accounts — in a single call. +description: Read and write an agent's full setup — brand, personality, inspiration sources, voice — in a single flat call. --- import { Aside } from '@astrojs/starlight/components'; -The Agent Core API exposes the agent setup canvas (the page you see at `gen.pro/{agent_id}/setup`) as a flat, PAT-authenticated API. One call reads or writes every section: identity, overview (brand profile), personality, inspiration, voice, look, and the agent's own social accounts. +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 the legacy per-resource sequence of TrendPulse endpoints. +**Use this when you want to configure an agent programmatically.** It replaces having to call multiple endpoints separately. +## Fields + +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. + +## Important distinctions + +**`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 the full agent setup ``` GET /v1/agents/{agent_id}/core ``` -Returns every section in one response. +Returns every field as a flat object. Fields that haven't been set return `null` or empty arrays. **Response 200:** + ```json { - "identity": { - "name": "Santiago", - "profile_photo_url": "https://cdn.gen.pro/..." - }, - "overview": { - "agent_id": "abc123", - "brand_name": "Santiago", - "description": "Santiago is a San Antonio-based street food scout...", - "identity_type": "character", - "goal": "growth", - "keywords": ["streetfood", "foodtruck", "tacotok"], - "target_platforms": ["tiktok", "instagram"], - "shortform": true, - "longform": false, - "primary_format": null, - "onboarding_status": "active" - }, - "personality": "Santiago grew up eating at his tia's taco stand...", - "inspiration": [ - { "id": 1, "url": "https://tiktok.com/@keilapacheco", "platform": "tiktok" } + "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" } ], - "voice": { + "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 🌮" } + ], + "look": "Warm earthy tones, close-up food shots, natural light", + "content_themes": ["street food", "local spots", "food reviews"], + "persona_inspirations": ["@keilapacheco", "@thetacochronicles"], + "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" }, - "look": { - "description": "Latino male early 30s, athletic build...", - "reference_images": [ - { "id": 11, "url": "https://cdn.gen.pro/refs/1.png" } - ] - }, - "accounts": [ - { "id": 42, "url": "https://tiktok.com/@santiago_real", "platform": "tiktok" } - ] + "name": "Santiago Real", + "profile_photo_url": "https://cdn.gen.pro/avatars/abc123.jpg", + "timezone": "America/Chicago", + "eleven_lab_api_key": null } ``` +Fields marked with a `*` below are sourced from the Rails agent record and returned alongside the local brand profile fields. If Rails is unreachable, they return `null` — the rest of the response still succeeds. + ## Update the agent setup ``` PATCH /v1/agents/{agent_id}/core ``` -Send any combination of sections. Semantics vary per section: +Flat merge-patch body. Send only the fields you want to change — omitted fields are left unchanged. + +### Field-level semantics -| Section | Semantics | +| Field | Semantics | |---|---| -| `identity` | **Merge** — only provided fields are updated | -| `overview` | **Merge** — only provided fields | -| `personality` | **Replace** — string replaces the full personality | -| `inspiration` | **Replace** — array replaces the full inspiration list | -| `voice` | **Replace** — `{voice_id, source}` sets the default voice | -| `look.description` | **Merge** — only updates the description (use item-level endpoints below for reference images) | -| `accounts` | **Replace** — array replaces the full account list | +| Scalars (`brand_name`, `description`, `identity_type`, `goal`, `shortform`, `longform`, `onboarding_status`, `personality`, `look`) | Replace | +| `keywords` | Replace the full list | +| `target_platforms` | Replace the full list | +| `content_themes` | Replace the full list | +| `persona_inspirations` | Replace the full list | +| `monitored` | **Replace the full list** — items you don't send are deleted | +| `research_topics` | **Replace the full list** — topics you don't send are deleted. Max 20 items. | +| `linked_accounts` | **Replace the full list** — rows you don't send are deleted. Rows with `id` are updated in place; rows without `id` are inserted. | +| `default_user_voice` | Replace — forwarded to the Rails agent record | +| `timezone` | Replace — IANA timezone string (e.g. `"America/Chicago"`). Forwarded to the Rails agent record. | +| `eleven_lab_api_key` | Replace — ElevenLabs API key for AURA voice connection. Send `null` to disconnect. Forwarded to the Rails agent record. | -**Response 200** (all sections succeeded) or **207 Multi-Status** (partial failure) with per-section results: +To add a single `monitored` item or `linked_account` without touching others, `GET /core` first, append, then `PATCH` with the full list. -```json -{ - "identity": { "status": "ok", "data": { "name": "Santiago", "profile_photo_url": null } }, - "overview": { "status": "ok", "data": { ... } }, - "inspiration": { "status": "error", "error": "invalid url" } -} -``` +### Validation guardrails + +- Unknown fields return **422** (`extra="forbid"` on every model). +- `description` max 500 chars. +- `personality` max 20000 chars. +- `look` max 2000 chars. +- `goal` max 100 chars. +- `timezone` max 100 chars (IANA format, e.g. `"America/Chicago"`). +- `eleven_lab_api_key` max 100 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. -**Example — set up a character in one call:** +### Keyword → monitored auto-mirror + +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. + +### Response + +`200` with the full updated agent core object (same shape as `GET /core`). If an upstream Rails write fails (e.g. saving `timezone` or `eleven_lab_api_key` to the Rails agent record), the failure is logged but does **not** affect the HTTP status — local fields are always saved first. + +## Example — set up a character in one call ```bash curl -X PATCH https://api.gen.pro/v1/agents/abc123/core \ -H "X-API-Key: $GEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ - "identity": { "name": "Santiago" }, - "overview": { - "brand_name": "Santiago", - "description": "Santiago is a San Antonio street food scout...", - "identity_type": "character", - "goal": "growth", - "keywords": ["streetfood", "tacotok"], - "target_platforms": ["tiktok", "instagram"], - "shortform": true - }, - "personality": "Santiago grew up eating at his tia'\''s taco stand...", - "inspiration": [ - { "url": "https://tiktok.com/@keilapacheco", "platform": "tiktok" } + "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" } ], - "look": { "description": "Latino male early 30s, athletic build" }, - "voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "source": "public" }, - "accounts": [ - { "url": "https://tiktok.com/@santiago_real", "platform": "tiktok" } - ] + "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.", + "look": "Warm earthy tones, close-up food shots, natural light", + "content_themes": ["street food", "local spots", "food reviews"], + "persona_inspirations": ["@keilapacheco", "@thetacochronicles"], + "timezone": "America/Chicago", + "default_user_voice": { "voice_id": "21m00Tcm4TlvDq8ikWAM", "source": "public" } }' ``` -## Identity — profile photo upload (multipart) +**TypeScript SDK equivalent:** -The photo can't fit in a JSON PATCH, so it has its own endpoint. +```typescript +import { GenClient } from '@poweredbygen/gen-sdk'; -``` -POST /v1/agents/{agent_id}/core/identity/profile-photo -Content-Type: multipart/form-data -``` +const gen = new GenClient({ apiKey: process.env.GEN_API_KEY! }); -**Form fields:** -- `file` — JPEG, PNG, or WebP +const result = await gen.updateAgentCore('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' }, +}); -**Response 200:** -```json -{ "profile_photo_url": "https://cdn.gen.pro/agents/abc123/photo.png" } +console.log(result.agent_id, result.brand_name); ``` -## Item-level endpoints - -Add or remove a single inspiration source, social account, or reference image without replacing the whole list. - -### Inspiration - -- `POST /v1/agents/{agent_id}/core/inspiration` — body `{"url": "...", "platform": "tiktok"}` -- `DELETE /v1/agents/{agent_id}/core/inspiration/{id}` - -### Accounts (the agent's own socials) - -- `POST /v1/agents/{agent_id}/core/accounts` — body `{"url": "...", "platform": "tiktok", "display_name": "..."}` (platform auto-detected from URL if omitted) -- `DELETE /v1/agents/{agent_id}/core/accounts/{id}` - -### Look reference images - -- `POST /v1/agents/{agent_id}/core/look/reference-images` — body `{"url": "..."}` -- `DELETE /v1/agents/{agent_id}/core/look/reference-images/{id}` - ## Errors | Code | Meaning | |---|---| -| 400 | Missing required field (e.g. POST inspiration without `url`) | | 401 | Missing or invalid `X-API-Key` | -| 404 | Agent not found, or item-level resource not found | -| 207 | `PATCH /core` — partial success. Inspect per-section `status` in the response body. | +| 403 | Valid PAT but the agent belongs to a different user | +| 404 | Agent config not found | +| 422 | Validation error — unknown field, bad enum, length cap exceeded, or missing required sub-field like `monitored[].item_type` | + +## Related -See also the [Voice API reference](/reference/agent-voice/) for voice library, cloning, design, and preview. +- [Voice API](/reference/agent-voice/) — voice library, design, cloning, preview (separate endpoint tree) +- [Discovery API](/reference/discovery/) — listing agents, workspaces, organizations diff --git a/src/content/docs/reference/agent-voice.mdx b/src/content/docs/reference/agent-voice.mdx index 0769892..bcc6912 100644 --- a/src/content/docs/reference/agent-voice.mdx +++ b/src/content/docs/reference/agent-voice.mdx @@ -1,14 +1,14 @@ --- title: Voice -description: List, design, clone, preview, and manage voices on an agent. Powered by the Agent Core service. +description: List, design, clone, preview, and manage voices on an agent. --- import { Aside } from '@astrojs/starlight/components'; -The Voice API lives alongside Agent Core under `/v1/agents/{agent_id}/voice/*`. It unifies voice library listing, ElevenLabs integration, prompt-based voice design, audio sample cloning, TTS preview, and voice deletion. +The Voice API lives alongside Agent Core under `/v1/agents/{agent_id}/voice/*`. It unifies voice library listing, prompt-based voice design, audio sample cloning, TTS preview, and voice deletion. ## List voices diff --git a/src/content/docs/reference/overview.mdx b/src/content/docs/reference/overview.mdx index fbda862..a73e4a5 100644 --- a/src/content/docs/reference/overview.mdx +++ b/src/content/docs/reference/overview.mdx @@ -17,22 +17,31 @@ All paths in this reference are relative to the base URL. For example, `GET /v1/ ## Authentication -The API supports two authentication methods, sent via HTTP headers: +Every API request requires authentication via one of these headers: -| Header | Description | +| Header | When to use | |--------|-------------| -| `X-API-Key` | A Personal Access Token (PAT) created in the GEN dashboard or via the [API Keys](/reference/api-keys/) endpoints. | -| `Authorization` | A `Bearer` JWT issued by Clerk or Dynamic XYZ (used by the GEN web app). | - -For programmatic integrations, use `X-API-Key`. For browser-based apps that already authenticate through GEN's login flow, use `Authorization: Bearer `. +| `X-API-Key` | **Recommended for all integrations.** A Personal Access Token (PAT) you create in the GEN dashboard. | +| `Authorization: Bearer ` | Only if your app already uses GEN's browser login flow. | ```bash curl https://api.gen.pro/v1/me \ -H "X-API-Key: your-api-key" ``` +### How to get an API key + +1. Log in to [gen.pro](https://gen.pro) +2. Select an agent (or create one) +3. Go to the **API** page in the left sidebar (`gen.pro/{agent_id}/api`) +4. Click **Create API Key**, give it a name, and copy the token + +That token is your `X-API-Key`. Store it securely — it won't be shown again. + +You can also create and manage API keys programmatically via the [API Keys endpoints](/reference/api-keys/). + ## Agent scoping diff --git a/src/content/docs/reference/rendering.mdx b/src/content/docs/reference/rendering.mdx index 2e2f4de..fd26af4 100644 --- a/src/content/docs/reference/rendering.mdx +++ b/src/content/docs/reference/rendering.mdx @@ -107,7 +107,7 @@ pending → processing → completed | failed | Status | Description | |--------|-------------| -| `pending` | Queued, waiting for Remotion Lambda to pick it up. | +| `pending` | Queued, waiting for the render pipeline to pick it up. | | `processing` | Video is being rendered. | | `completed` | Done. `url` contains the final video CDN link. | | `failed` | Render failed (check logs). |