{"openapi":"3.1.0","info":{"title":"Animam API","version":"2.1.0","description":"REST API for Animam.ai — multi-tenant AI agent infrastructure. Schema-first: this spec is generated from the same Zod schemas that validate runtime requests in apps/api and that power the MCP server in apps/mcp. See https://animam.ai/llms-full.txt for full agent-readable documentation.","contact":{"name":"Animam","url":"https://animam.ai"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://api.animam.ai","description":"Production"}],"externalDocs":{"url":"https://animam.ai/llms-full.txt","description":"Agent-readable full documentation (Markdown)."},"tags":[{"name":"corpus","description":"Knowledge base CRUD."},{"name":"tenants","description":"Tenant settings, persona, MCP tools config."},{"name":"segments","description":"Conversation segments (audiences)."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key (ak_...) or JWT","description":"Static API key (`Authorization: Bearer ak_xxx`) or short-lived JWT issued by /oauth/token."},"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Alternative header for legacy integrations."},"oauth2":{"type":"oauth2","description":"OAuth 2.1 + PKCE for MCP-style agent integrations.","flows":{"authorizationCode":{"authorizationUrl":"https://api.animam.ai/oauth/authorize","tokenUrl":"https://api.animam.ai/oauth/token","scopes":{"corpus:read":"Read knowledge base entries","corpus:write":"Create / update / delete knowledge base entries","segments:read":"Read segments","segments:write":"Create / update segments","settings:read":"Read tenant settings","settings:write":"Update tenant settings","stats:read":"Read analytics and usage data"}}}}},"schemas":{"CorpusEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"content":{"type":"string"},"summary":{"type":["string","null"]},"priority":{"type":"integer"},"isActive":{"type":"boolean"},"externalId":{"type":["string","null"]},"source":{"type":["string","null"]},"sourceUrl":{"type":["string","null"]},"segment":{"type":["object","null"],"properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","content","summary","priority","isActive","externalId","source","sourceUrl","segment","createdAt","updatedAt"]},"ListCorpusResponse":{"type":"object","properties":{"corpus":{"type":"array","items":{"$ref":"#/components/schemas/CorpusEntry"}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}},"required":["page","limit","total","totalPages"]}},"required":["corpus","pagination"]},"SegmentSummary":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"corpusCount":{"type":"integer"},"conversationsCount":{"type":"integer"}},"required":["slug","name","isDefault","isActive","corpusCount","conversationsCount"]},"FormField":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"type":{"type":"string","enum":["text","email","phone","textarea"]},"required":{"type":"boolean"}},"required":["name","label","type","required"]},"FormConfig":{"type":["object","null"],"properties":{"enabled":{"type":"boolean"},"intent":{"type":"string"},"confirmationMessage":{"type":"string"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FormField"}}},"required":["enabled","intent","fields"]},"SegmentDetail":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"systemPrompt":{"type":"string"},"welcomeMessage":{"type":["string","null"]},"formConfig":{"$ref":"#/components/schemas/FormConfig"},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"}},"required":["slug","name","systemPrompt","welcomeMessage","formConfig","isDefault","isActive"]},"BotTone":{"type":"string","enum":["FORMAL","NEUTRAL","FRIENDLY","PLAYFUL","TECHNICAL"]},"ContentPolicy":{"type":"string","enum":["SFW_STRICT","SFW","MODERATE","UNFILTERED"]},"TenantPublic":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"botName":{"type":["string","null"]},"botTitle":{"type":["string","null"]},"botLore":{"type":["string","null"]},"botTone":{"$ref":"#/components/schemas/BotTone"},"useTutoiement":{"type":"boolean"},"allowEmojis":{"type":"boolean"},"botAvatar":{"type":["string","null"]},"avatarType":{"type":["string","null"]},"avatarBackground":{"type":["string","null"]},"primaryColor":{"type":"string"},"secondaryColor":{"type":["string","null"]},"showAiBadge":{"type":"boolean"},"plan":{"type":"string"},"conversationsLimit":{"type":"integer"},"conversationsUsed":{"type":"integer"},"segmentsLimit":{"type":"integer"},"corpusLimit":{"type":"integer"},"allowedDomains":{"type":"array","items":{"type":"string"}},"contentPolicy":{"$ref":"#/components/schemas/ContentPolicy"},"mcpToolsUrl":{"type":["string","null"]},"mcpToolsMaxTools":{"type":"integer"}},"required":["slug","name","botName","botTitle","botLore","botTone","useTutoiement","allowEmojis","botAvatar","avatarType","avatarBackground","primaryColor","secondaryColor","showAiBadge","plan","conversationsLimit","conversationsUsed","segmentsLimit","corpusLimit","allowedDomains","contentPolicy","mcpToolsUrl","mcpToolsMaxTools"]},"McpToolsConfig":{"type":"object","properties":{"mcpToolsUrl":{"type":["string","null"]},"mcpToolsMaxTools":{"type":"integer"},"hasApiKey":{"type":"boolean"},"plan":{"type":"string"}},"required":["mcpToolsUrl","mcpToolsMaxTools","hasApiKey"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"details":{},"code":{"type":"string"}},"required":["error"]},"CreateCorpusBody":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255,"example":"Opening hours"},"content":{"type":"string","minLength":1,"description":"Full body of the entry."},"summary":{"type":"string","maxLength":500,"description":"Short summary surfaced in prompt; full content fetched on-demand via EXPLORE_CORPUS."},"segmentSlug":{"type":"string"},"priority":{"type":"integer","minimum":0,"maximum":100,"example":0},"isActive":{"type":"boolean","example":true},"externalId":{"type":"string","maxLength":255,"description":"Idempotency key from caller (e.g. wp-post-42). Upserts on conflict."},"source":{"type":"string","maxLength":50},"sourceUrl":{"type":"string","maxLength":500,"format":"uri"},"locale":{"type":"string","maxLength":10,"pattern":"^[a-z]{2}(-[a-zA-Z]{2})?$","description":"2-letter ISO language code of the entry (en, fr, es, …). Used to scope retrieval per visitor locale on multilingual sites."}},"required":["title","content"]},"BulkCorpusBody":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/CreateCorpusBody"},"minItems":1,"maxItems":50,"description":"Up to 50 entries per call. Idempotent on externalId."}},"required":["entries"]},"SyncCorpusBody":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://example.com/faq"},"title":{"type":"string","maxLength":255},"segmentSlug":{"type":"string"}},"required":["url"]},"UpdateCorpusBody":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255},"content":{"type":"string","minLength":1},"summary":{"type":"string","maxLength":500},"priority":{"type":"integer","minimum":0,"maximum":100},"isActive":{"type":"boolean"},"segmentSlug":{"type":"string"}}},"UpdateTenantBody":{"type":"object","properties":{"botName":{"type":"string","maxLength":50},"botTitle":{"type":"string","maxLength":100},"botLore":{"type":"string"},"botTone":{"$ref":"#/components/schemas/BotTone"},"useTutoiement":{"type":"boolean"},"allowEmojis":{"type":"boolean"},"primaryColor":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},"secondaryColor":{"type":["string","null"],"pattern":"^#[0-9A-Fa-f]{6}$"},"showAiBadge":{"type":"boolean"},"allowedDomains":{"type":"array","items":{"type":"string","maxLength":255}},"contentPolicy":{"$ref":"#/components/schemas/ContentPolicy"}}},"UpdateMcpToolsBody":{"type":"object","properties":{"mcpToolsUrl":{"type":["string","null"],"maxLength":500,"format":"uri"},"mcpToolsApiKey":{"type":["string","null"],"maxLength":500},"mcpToolsMaxTools":{"type":"integer","minimum":1,"maximum":20}}},"CreateSegmentBody":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9-]+$"},"name":{"type":"string","minLength":1,"maxLength":255},"systemPrompt":{"type":"string","minLength":1},"welcomeMessage":{"type":"string"},"isDefault":{"type":"boolean"}},"required":["slug","name","systemPrompt"]},"UpdateSegmentBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"systemPrompt":{"type":"string","minLength":1},"welcomeMessage":{"type":"string"},"formConfig":{"allOf":[{"$ref":"#/components/schemas/FormConfig"},{"type":"object"}]},"isActive":{"type":"boolean"}}}},"parameters":{}},"paths":{"/tenants/{slug}/corpus":{"get":{"tags":["corpus"],"summary":"List corpus entries","description":"Paginated list of knowledge base entries for the tenant. Optionally filter by segment.","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["corpus:read"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","description":"Filter to a specific segment by slug."},"required":false,"name":"segmentSlug","in":"query"},{"schema":{"type":"integer","minimum":1,"example":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"example":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated corpus list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCorpusResponse"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["corpus"],"summary":"Create or upsert a corpus entry","description":"Creates a new entry. If `externalId` is provided, performs an idempotent upsert (designed for CMS sync flows like the WordPress plugin).","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["corpus:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCorpusBody"}}}},"responses":{"200":{"description":"Existing entry updated (upsert path).","content":{"application/json":{"schema":{"type":"object","properties":{"corpus":{"$ref":"#/components/schemas/CorpusEntry"},"created":{"type":"boolean","enum":[false]}},"required":["corpus","created"]}}}},"201":{"description":"Entry created.","content":{"application/json":{"schema":{"type":"object","properties":{"corpus":{"$ref":"#/components/schemas/CorpusEntry"},"created":{"type":"boolean"}},"required":["corpus"]}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Corpus limit reached for the current plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/tenants/{slug}/corpus/bulk":{"post":{"tags":["corpus"],"summary":"Bulk upsert corpus entries","description":"Up to 50 entries per call. Each entry must include `externalId` for idempotent upsert. Used by the WordPress plugin full-resync flow.","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["corpus:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCorpusBody"}}}},"responses":{"200":{"description":"Bulk operation summary.","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"},"updated":{"type":"integer"},"skipped":{"type":"integer"},"errors":{"type":"array","items":{"type":"object","properties":{"externalId":{"type":"string"},"reason":{"type":"string"}},"required":["externalId","reason"]}}},"required":["created","updated","skipped","errors"]}}}}}}},"/tenants/{slug}/corpus/sync":{"post":{"tags":["corpus"],"summary":"Import corpus entry from URL","description":"Fetches and converts a remote URL (HTML / MD / PDF) into a corpus entry.","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["corpus:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncCorpusBody"}}}},"responses":{"201":{"description":"Entry imported.","content":{"application/json":{"schema":{"type":"object","properties":{"corpus":{"$ref":"#/components/schemas/CorpusEntry"}},"required":["corpus"]}}}}}}},"/tenants/{slug}/corpus/{id}":{"get":{"tags":["corpus"],"summary":"Get a single corpus entry","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["corpus:read"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Corpus entry UUID."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Corpus entry.","content":{"application/json":{"schema":{"type":"object","properties":{"corpus":{"$ref":"#/components/schemas/CorpusEntry"}},"required":["corpus"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["corpus"],"summary":"Update a corpus entry","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["corpus:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Corpus entry UUID."},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCorpusBody"}}}},"responses":{"200":{"description":"Updated entry.","content":{"application/json":{"schema":{"type":"object","properties":{"corpus":{"$ref":"#/components/schemas/CorpusEntry"}},"required":["corpus"]}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["corpus"],"summary":"Delete a corpus entry","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["corpus:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Corpus entry UUID."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}}}}},"/tenants/{slug}":{"get":{"tags":["tenants"],"summary":"Get tenant info and settings","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["settings:read"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Tenant info, persona, plan, recent conversations.","content":{"application/json":{"schema":{"type":"object","properties":{"tenant":{"$ref":"#/components/schemas/TenantPublic"},"stats":{"type":"object","properties":{"corpusCount":{"type":"integer"},"segmentCount":{"type":"integer"},"conversationCount":{"type":"integer"},"conversationsRemaining":{"type":"integer"}},"required":["corpusCount","segmentCount","conversationCount","conversationsRemaining"]},"recentConversations":{"type":"array","items":{}},"embedCode":{"type":"string"}},"required":["tenant","stats","recentConversations","embedCode"]}}}}}},"put":{"tags":["tenants"],"summary":"Update tenant settings","description":"Update bot persona, content policy, and integrations. Schema reflects the MCP-shared subset; additional REST-only fields (turnstile, visitor auth, booking CTA, plan limits) accepted but not yet documented here. See https://animam.ai/llms-full.txt for full reference.","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["settings:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTenantBody"}}}},"responses":{"200":{"description":"Updated tenant.","content":{"application/json":{"schema":{"type":"object","properties":{"tenant":{"$ref":"#/components/schemas/TenantPublic"}},"required":["tenant"]}}}}}}},"/tenants/{slug}/mcp-tools":{"put":{"tags":["tenants"],"summary":"Configure external MCP server as tool source","description":"Pro+ only. URL must be HTTPS. Tools are discovered via tools/list and proxied via tools/call (cached 5min).","security":[{"bearerAuth":[]},{"oauth2":["settings:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMcpToolsBody"}}}},"responses":{"200":{"description":"Updated MCP tools config.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpToolsConfig"}}}}}}},"/tenants/{slug}/segments":{"get":{"tags":["segments"],"summary":"List segments","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["segments:read"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"List of segments.","content":{"application/json":{"schema":{"type":"object","properties":{"segments":{"type":"array","items":{"$ref":"#/components/schemas/SegmentSummary"}}},"required":["segments"]}}}}}},"post":{"tags":["segments"],"summary":"Create a segment","description":"Creates a new segment. The REST endpoint auto-generates a slug from `name`; pass `slug` explicitly only via the MCP tool.","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["segments:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSegmentBody"}}}},"responses":{"201":{"description":"Created segment.","content":{"application/json":{"schema":{"type":"object","properties":{"segment":{"$ref":"#/components/schemas/SegmentDetail"}},"required":["segment"]}}}}}}},"/tenants/{slug}/segments/{slugOrId}":{"put":{"tags":["segments"],"summary":"Update a segment","description":"Path identifier is `segmentId` (UUID) on the REST endpoint or `slug` via MCP. Both refer to the same segment.","security":[{"bearerAuth":[]},{"apiKey":[]},{"oauth2":["segments:write"]}],"parameters":[{"schema":{"type":"string","description":"Tenant slug (URL-safe identifier).","example":"dr-martin"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","description":"Segment UUID (REST) or slug (MCP)."},"required":true,"name":"slugOrId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSegmentBody"}}}},"responses":{"200":{"description":"Updated segment.","content":{"application/json":{"schema":{"type":"object","properties":{"segment":{"$ref":"#/components/schemas/SegmentDetail"}},"required":["segment"]}}}}}}}},"webhooks":{}}