MCP App Store

Overview

SmartTalks lets you query, edit, and analyze your SmartTalks workspace from ChatGPT. Look up users, contacts, accounts, agent queues, conversation history, scheduling, contracts, and CRM records; inspect templates, workflows, and campaign configurations; and create, update, or delete records when you need to keep data clean. Every action runs under your existing SmartTalks login and permissions, and read-only lookups are split from mutating actions so changes can be reviewed before they run.

Tools

smarttalks_accounts

ChatGPT
Accounts, SUB-TENANTS under a Group. Every account is scoped to one Group and carries all product data (Users, Tags, Units, PermissionsGroups, Tokens). Accounts are provisioned externally; only get/list/update are exposed here. IDENTITY & TENANCY _id (ObjectId) — primary key groupId (ObjectId) — parent Group that owns this account (see smarttalks_groups) internalIdentifier (string) — optional internal reference code PROFILE name (string, required) — display name for the account company (string, required) — legal company name cnpj (string, required) — Brazilian tax registration number (CNPJ) avatar (string, default "") — URL to account logo/avatar image address (object) — free-form postal address object INFRASTRUCTURE server (enum, default "nyc") — deployment region for this account's data: "ams" = Amsterdam | "blr" = Bangalore | "fra" = Frankfurt "lon" = London | "nyc" = New York | "sfo" = San Francisco "sgp" = Singapore | "tor" = Toronto | "pt" = Portugal | "br" = Brazil lang (enum, default "pt-BR") — default UI locale for users in this account: "pt-BR" = Brazilian Portuguese | "en-US" = English (US) "es-ES" = Spanish | "pt-PT" = European Portuguese bucket (string) — cloud storage bucket identifier assigned to this account PERMISSIONS (sub-object) — feature and capacity limits for the account permissions.users (number, default 5) — max licensed user seats permissions.usersConnected (number, default 3) — max simultaneous logged-in users permissions.finishLiveAlert (boolean, default false) — whether a warning alert fires when a live interaction is about to be closed permissions.finishLiveAlertMessage (string, default "") — custom message shown in the finish-live alert dialog permissions.liveExpirationAlert (number, default 5) — minutes before a live interaction expires to trigger an expiration alert BILLING & LIFECYCLE credits (number, default 0) — credits consumed during the current billing period; incremented by consumeCredits, reset each cycle via resetCredits cancellationRequestAt (Date) — timestamp when a cancellation was requested cancellationRequestReason (string) — free-text reason provided at cancellation request deactivatedAt (Date) — timestamp when the account was deactivated status (boolean, default true) — soft-delete flag; list queries automatically filter to status:true AUDIT createdAt, updatedAt (Date) — auto-managed timestamps createdBy (ObjectId) — User who created the account (see smarttalks_users) updatedBy (ObjectId) — User who last updated the account (see smarttalks_users) deletedBy (ObjectId) — User who soft-deleted the account (see smarttalks_users) RELATIONSHIPS Group 1 ── N Account.groupId (see smarttalks_groups; use smarttalks_groups.list to enumerate all accounts under a group) Account 1 ── N Users — query via smarttalks_users.list { query: { accountId } } Account 1 ── N PermissionsGroups — query via smarttalks_permissions_groups.list { query: { accountId } } Account 1 ── N Units — query via smarttalks_units.list { query: { accountId } } Account 1 ── N Tags — query via smarttalks_tags.list { query: { accountId } } Account 1 ── N Tokens — query via smarttalks_tokens.list { query: { accountId } } ACTIONS get { id } — fetch one account by _id list { query, project?, options? } — Mongo filter. Common: { groupId: "<groupId>" } — all accounts under a specific group { server: "br" } — accounts on Brazil region { server: "nyc", status: true } — active accounts on New York region { lang: "pt-BR" } — accounts with Brazilian Portuguese locale options supports { limit, skip, sort } update { id, payload } — mutate any mutable field (profile, permissions, lang, server, etc.) NOT EXPOSED create/delete — Accounts are provisioned via billing/admin flows elsewhere. These operations are not available through this tool.

smarttalks_accounts_manage

ChatGPT
Accounts, SUB-TENANTS under a Group. Every account is scoped to one Group and carries all product data (Users, Tags, Units, PermissionsGroups, Tokens). Accounts are provisioned externally; only get/list/update are exposed here. IDENTITY & TENANCY _id (ObjectId) — primary key groupId (ObjectId) — parent Group that owns this account (see smarttalks_groups) internalIdentifier (string) — optional internal reference code PROFILE name (string, required) — display name for the account company (string, required) — legal company name cnpj (string, required) — Brazilian tax registration number (CNPJ) avatar (string, default "") — URL to account logo/avatar image address (object) — free-form postal address object INFRASTRUCTURE server (enum, default "nyc") — deployment region for this account's data: "ams" = Amsterdam | "blr" = Bangalore | "fra" = Frankfurt "lon" = London | "nyc" = New York | "sfo" = San Francisco "sgp" = Singapore | "tor" = Toronto | "pt" = Portugal | "br" = Brazil lang (enum, default "pt-BR") — default UI locale for users in this account: "pt-BR" = Brazilian Portuguese | "en-US" = English (US) "es-ES" = Spanish | "pt-PT" = European Portuguese bucket (string) — cloud storage bucket identifier assigned to this account PERMISSIONS (sub-object) — feature and capacity limits for the account permissions.users (number, default 5) — max licensed user seats permissions.usersConnected (number, default 3) — max simultaneous logged-in users permissions.finishLiveAlert (boolean, default false) — whether a warning alert fires when a live interaction is about to be closed permissions.finishLiveAlertMessage (string, default "") — custom message shown in the finish-live alert dialog permissions.liveExpirationAlert (number, default 5) — minutes before a live interaction expires to trigger an expiration alert BILLING & LIFECYCLE credits (number, default 0) — credits consumed during the current billing period; incremented by consumeCredits, reset each cycle via resetCredits cancellationRequestAt (Date) — timestamp when a cancellation was requested cancellationRequestReason (string) — free-text reason provided at cancellation request deactivatedAt (Date) — timestamp when the account was deactivated status (boolean, default true) — soft-delete flag; list queries automatically filter to status:true AUDIT createdAt, updatedAt (Date) — auto-managed timestamps createdBy (ObjectId) — User who created the account (see smarttalks_users) updatedBy (ObjectId) — User who last updated the account (see smarttalks_users) deletedBy (ObjectId) — User who soft-deleted the account (see smarttalks_users) RELATIONSHIPS Group 1 ── N Account.groupId (see smarttalks_groups; use smarttalks_groups.list to enumerate all accounts under a group) Account 1 ── N Users — query via smarttalks_users.list { query: { accountId } } Account 1 ── N PermissionsGroups — query via smarttalks_permissions_groups.list { query: { accountId } } Account 1 ── N Units — query via smarttalks_units.list { query: { accountId } } Account 1 ── N Tags — query via smarttalks_tags.list { query: { accountId } } Account 1 ── N Tokens — query via smarttalks_tokens.list { query: { accountId } } ACTIONS get { id } — fetch one account by _id list { query, project?, options? } — Mongo filter. Common: { groupId: "<groupId>" } — all accounts under a specific group { server: "br" } — accounts on Brazil region { server: "nyc", status: true } — active accounts on New York region { lang: "pt-BR" } — accounts with Brazilian Portuguese locale options supports { limit, skip, sort } update { id, payload } — mutate any mutable field (profile, permissions, lang, server, etc.) NOT EXPOSED create/delete — Accounts are provisioned via billing/admin flows elsewhere. These operations are not available through this tool.

smarttalks_addons

ChatGPT
Addons — REUSABLE FLOW STEP DEFINITIONS. An addon is a named, tenant-scoped block-template (e.g. a question, CSAT, smart_gpt step, HSM template) that Flows pull in by reference. At runtime, the flow engine loads the addon document, dispatches on type, and delegates to the matching handler. The handler reads options/option for its per-type config, validates user input, and decides what to do next — capture a value, write a CRM field, branch to a block, queue to an operator, finish the interaction, call an LLM, etc. Every addon belongs to ONE Group and ONE Account; tenancy is enforced server-side. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier (shares the "flows" counter sequence) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters to status:true, so deleted addons are invisible to normal queries createdAt, updatedAt (Date) — managed automatically All changes are captured in the Audits collection via a MongoDB change-stream watch. COMMON FIELDS (all addon types) label (string, required) — display name in the flow builder caption (string, default "") — short description / sub-label type (string, required, enum) — see TYPE SEMANTICS below for the full list and what each does errorMessage (string) — message shown to the end-user when validation fails on this block showInLive (boolean, default true) — captured value visible in the Live operator view isRequired (boolean, default false) — user must supply a value before the flow advances isEvidence (boolean, default true) — record the captured value as "evidence" on the interaction openFieldToFill (boolean, default false) — hybrid list/open input default (string) — default value if the user provides none warning (boolean) — UI flag marking the addon as needing operator attention (read-only in most flows; not mutated by the engine) entityId (ObjectId) — ERM Entity reference (for entity-bound types like erm_list, register_erm, erm_content) fieldId (ObjectId) — ERM Field reference (for field-bound types) PER-TYPE CONFIG (options / option) options is a typed Array. option is a Mixed single-value. The shape of each depends on type — the Vue builder at App/src/components/dialogs/addons/createAddon.vue is the authoritative source. Key shapes: button options: [{ label, blockId, contextIndex? }] — selectable buttons list options: [{ label, description?, conditions?, blockId? }] — rich list with per-row conditions plain_list options: [{ label, blockId, contextIndex? }] — simple numbered menu sectioned_list same as plain_list csat options: [{ value (1..10), description, blockId? }] — satisfaction scale opt-in options: [{ label, blockId? }] (first entry = TRUE / opt-in, second = FALSE / opt-out) SIDE-EFFECT: writes the boolean to CRM field 37 (hardcoded). condition options: [{ operation ("crm"|"evidence"|"channel"|"units"|"date"), path, comparator, value, blockId }] hashtag options: [{ label, jumpType ("block"|"queue"|"continue"), blockId?, queueId?, unitId? }] hsm option: { templateId, headerVariables[], bodyVariables[], buttonsVariables[], awaitResponse, jump[] } PER CHANNEL (HSM.js looks up by channel._id) smart_intent option: { modelId, categories: [{ category, response, type ("block"|"queue"|"continue"), blockId?, queueId? }] } smart_gpt / parse_gpt option: { modelId, awaitResponse, fixedInput, overrideSystemPrompt, systemPrompt } smart_docs option: { modelId, … } — FAQ/docs Q&A via embeddings; validates answer hallucination smart_content option: { modelId, overrideSystemPrompt, systemPrompt, shouldValidate, exceptions[...] } booking option: { customDomain, shouldShorten, expirableUrl, notFoundMessage } list_schedules / list_appointments option: { widgetId, applyConditionals, tags, mode ("plainlist"|"summary"), scheduleStatus } Simple validator types (cpf, email, name, phone, date, number, regex, time) typically have no options/option beyond default/errorMessage. EXPIRATION (inactivity timeout when awaiting the user's reply) expiration.time (number) — duration, typically MINUTES (UI constrains 5..10080 = up to 7 days) expiration.finishOnExpiration (boolean) — finish the interaction instead of jumping expiration.jumpTo (ObjectId) — block id to jump to when the timer fires (ignored if finishOnExpiration=true) ERROR-COUNT (validation-retry policy — branches when the user repeatedly gives invalid input) errorCount.isActive (boolean) — enable per-addon counter (otherwise falls back to the flow's globalActions.errorCount) errorCount.limit (number, 0..15) — max invalid attempts before the branch fires errorCount.jumpType (enum: "block" | "queue" | "continue") "block" → jump to errorCount.blockId "queue" → send to errorCount.queueId (smarttalks_queues) "continue" → advance the flow past this block errorCount.blockId (ObjectId) — target for jumpType="block" errorCount.queueId (ObjectId) — target for jumpType="queue" (smarttalks_queues) Engine increments a counter at refs.connectors[___addonError-{addonId}___] on each invalid input; once ≥ limit, dispatches the configured branch without sending the error message again. TYPE SEMANTICS (type → what it does at runtime) Flow-control: "flow", "block", "context", "live", "finish", "translate" Text validators: "name", "cpf", "email", "phone", "date", "time", "number", "regex", "question" Interactive menus: "button", "list", "plain_list", "sectioned_list", "unit_select" Media: "audio", "document", "image", "sticker", "video", "receive_media", "navigation" Commerce: "product", "product_list", "catalog", "carousel" Feedback / consent: "csat" (score), "opt-in" (writes CRM field 37) Keyword routing: "hashtag" (extracts #tag → block/queue/continue), "smart_intent" (ML intent classifier) AI generation: "smart_gpt" / "parse_gpt" (GPT completion), "smart_content" (templated gen with validation), "smart_docs" (FAQ via embeddings), "smartfaq" (legacy FAQ) WhatsApp HSM: "hsm" (per-channel template with header/body/button variables) Email: "push_email" (outbound), "email" (inbound validator) ERM: "erm_list", "erm_content", "register_erm" (read/write ERM entities via entityId/fieldId) Scheduling: "list_schedules", "list_appointments", "schedule_link", "booking" AI-addon note: smart_gpt / smart_content / smart_docs / parse_gpt return tokenUsage ({ input, output, cached }) alongside the answer; Engine records it on the interaction message. LIFECYCLE Soft delete only. Deleting an addon referenced by a live Flow does NOT hard-break the flow — the engine tolerates the missing doc and falls through — but the flow block will log a warning. Audit the Flows that reference an addon (search flows where blocks[].contexts[].addonId matches) before deleting. RELATIONSHIPS Group 1 ── N Addon.groupId Account 1 ── N Addon.accountId Addon N ── 1 ERM Entity via entityId Addon N ── 1 ERM Field via fieldId Addon referenced by Flows — flow blocks point to addons via Flow.blocks[].contexts[].addonId (the foreign key lives on the flow side) Addon.expiration.jumpTo / errorCount.blockId → block ids within Flows Addon.errorCount.queueId → smarttalks_queues ACTIONS get { id } — fetch one addon (auto-scoped) list { query, project?, options? } — returns { count, items[] }. Default options.limit=10, sorted by createdAt desc. Common shapes: { type: "smart_gpt" } — all AI-step addons { type: { $in: ["question", "list", "button"] } } — interactive input blocks { label: { $regex: "csat", $options: "i" } } — partial name search { entityId: "<entityId>" } — addons bound to a specific ERM entity { isRequired: true } — mandatory blocks { "errorCount.isActive": true } — addons with retry-branch configured create { payload } — required: label, type. Typically also: options/option (per type), errorCount{...}, expiration{...}, errorMessage. groupId/accountId server-injected. update { id, payload, project? } — any mutable field. Pass a non-empty project to get the updated doc back. delete { id } — soft-delete (status:false). Verify no active Flow references this addon before calling. EXAMPLES list { query: { type: "smart_gpt" }, options: { limit: 20, sort: { createdAt: -1 } } } create { payload: { label: "Classify intent", type: "smart_intent", errorMessage: "Sorry, could you rephrase?", option: { modelId: "<modelId>", categories: [ { category: "billing", type: "queue", queueId: "<qBilling>" }, { category: "sales", type: "block", blockId: "<bSales>" } ] }, errorCount: { isActive: true, limit: 2, jumpType: "queue", queueId: "<fallbackQ>" } } } create { payload: { label: "CSAT 1-5", type: "csat", options: [ { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, { value: 5 } ], isRequired: true } } update { id: "<addonId>", payload: { label: "Classify intent (v2)" } }

smarttalks_addons_manage

ChatGPT
Addons — REUSABLE FLOW STEP DEFINITIONS. An addon is a named, tenant-scoped block-template (e.g. a question, CSAT, smart_gpt step, HSM template) that Flows pull in by reference. At runtime, the flow engine loads the addon document, dispatches on type, and delegates to the matching handler. The handler reads options/option for its per-type config, validates user input, and decides what to do next — capture a value, write a CRM field, branch to a block, queue to an operator, finish the interaction, call an LLM, etc. Every addon belongs to ONE Group and ONE Account; tenancy is enforced server-side. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier (shares the "flows" counter sequence) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters to status:true, so deleted addons are invisible to normal queries createdAt, updatedAt (Date) — managed automatically All changes are captured in the Audits collection via a MongoDB change-stream watch. COMMON FIELDS (all addon types) label (string, required) — display name in the flow builder caption (string, default "") — short description / sub-label type (string, required, enum) — see TYPE SEMANTICS below for the full list and what each does errorMessage (string) — message shown to the end-user when validation fails on this block showInLive (boolean, default true) — captured value visible in the Live operator view isRequired (boolean, default false) — user must supply a value before the flow advances isEvidence (boolean, default true) — record the captured value as "evidence" on the interaction openFieldToFill (boolean, default false) — hybrid list/open input default (string) — default value if the user provides none warning (boolean) — UI flag marking the addon as needing operator attention (read-only in most flows; not mutated by the engine) entityId (ObjectId) — ERM Entity reference (for entity-bound types like erm_list, register_erm, erm_content) fieldId (ObjectId) — ERM Field reference (for field-bound types) PER-TYPE CONFIG (options / option) options is a typed Array. option is a Mixed single-value. The shape of each depends on type — the Vue builder at App/src/components/dialogs/addons/createAddon.vue is the authoritative source. Key shapes: button options: [{ label, blockId, contextIndex? }] — selectable buttons list options: [{ label, description?, conditions?, blockId? }] — rich list with per-row conditions plain_list options: [{ label, blockId, contextIndex? }] — simple numbered menu sectioned_list same as plain_list csat options: [{ value (1..10), description, blockId? }] — satisfaction scale opt-in options: [{ label, blockId? }] (first entry = TRUE / opt-in, second = FALSE / opt-out) SIDE-EFFECT: writes the boolean to CRM field 37 (hardcoded). condition options: [{ operation ("crm"|"evidence"|"channel"|"units"|"date"), path, comparator, value, blockId }] hashtag options: [{ label, jumpType ("block"|"queue"|"continue"), blockId?, queueId?, unitId? }] hsm option: { templateId, headerVariables[], bodyVariables[], buttonsVariables[], awaitResponse, jump[] } PER CHANNEL (HSM.js looks up by channel._id) smart_intent option: { modelId, categories: [{ category, response, type ("block"|"queue"|"continue"), blockId?, queueId? }] } smart_gpt / parse_gpt option: { modelId, awaitResponse, fixedInput, overrideSystemPrompt, systemPrompt } smart_docs option: { modelId, … } — FAQ/docs Q&A via embeddings; validates answer hallucination smart_content option: { modelId, overrideSystemPrompt, systemPrompt, shouldValidate, exceptions[...] } booking option: { customDomain, shouldShorten, expirableUrl, notFoundMessage } list_schedules / list_appointments option: { widgetId, applyConditionals, tags, mode ("plainlist"|"summary"), scheduleStatus } Simple validator types (cpf, email, name, phone, date, number, regex, time) typically have no options/option beyond default/errorMessage. EXPIRATION (inactivity timeout when awaiting the user's reply) expiration.time (number) — duration, typically MINUTES (UI constrains 5..10080 = up to 7 days) expiration.finishOnExpiration (boolean) — finish the interaction instead of jumping expiration.jumpTo (ObjectId) — block id to jump to when the timer fires (ignored if finishOnExpiration=true) ERROR-COUNT (validation-retry policy — branches when the user repeatedly gives invalid input) errorCount.isActive (boolean) — enable per-addon counter (otherwise falls back to the flow's globalActions.errorCount) errorCount.limit (number, 0..15) — max invalid attempts before the branch fires errorCount.jumpType (enum: "block" | "queue" | "continue") "block" → jump to errorCount.blockId "queue" → send to errorCount.queueId (smarttalks_queues) "continue" → advance the flow past this block errorCount.blockId (ObjectId) — target for jumpType="block" errorCount.queueId (ObjectId) — target for jumpType="queue" (smarttalks_queues) Engine increments a counter at refs.connectors[___addonError-{addonId}___] on each invalid input; once ≥ limit, dispatches the configured branch without sending the error message again. TYPE SEMANTICS (type → what it does at runtime) Flow-control: "flow", "block", "context", "live", "finish", "translate" Text validators: "name", "cpf", "email", "phone", "date", "time", "number", "regex", "question" Interactive menus: "button", "list", "plain_list", "sectioned_list", "unit_select" Media: "audio", "document", "image", "sticker", "video", "receive_media", "navigation" Commerce: "product", "product_list", "catalog", "carousel" Feedback / consent: "csat" (score), "opt-in" (writes CRM field 37) Keyword routing: "hashtag" (extracts #tag → block/queue/continue), "smart_intent" (ML intent classifier) AI generation: "smart_gpt" / "parse_gpt" (GPT completion), "smart_content" (templated gen with validation), "smart_docs" (FAQ via embeddings), "smartfaq" (legacy FAQ) WhatsApp HSM: "hsm" (per-channel template with header/body/button variables) Email: "push_email" (outbound), "email" (inbound validator) ERM: "erm_list", "erm_content", "register_erm" (read/write ERM entities via entityId/fieldId) Scheduling: "list_schedules", "list_appointments", "schedule_link", "booking" AI-addon note: smart_gpt / smart_content / smart_docs / parse_gpt return tokenUsage ({ input, output, cached }) alongside the answer; Engine records it on the interaction message. LIFECYCLE Soft delete only. Deleting an addon referenced by a live Flow does NOT hard-break the flow — the engine tolerates the missing doc and falls through — but the flow block will log a warning. Audit the Flows that reference an addon (search flows where blocks[].contexts[].addonId matches) before deleting. RELATIONSHIPS Group 1 ── N Addon.groupId Account 1 ── N Addon.accountId Addon N ── 1 ERM Entity via entityId Addon N ── 1 ERM Field via fieldId Addon referenced by Flows — flow blocks point to addons via Flow.blocks[].contexts[].addonId (the foreign key lives on the flow side) Addon.expiration.jumpTo / errorCount.blockId → block ids within Flows Addon.errorCount.queueId → smarttalks_queues ACTIONS get { id } — fetch one addon (auto-scoped) list { query, project?, options? } — returns { count, items[] }. Default options.limit=10, sorted by createdAt desc. Common shapes: { type: "smart_gpt" } — all AI-step addons { type: { $in: ["question", "list", "button"] } } — interactive input blocks { label: { $regex: "csat", $options: "i" } } — partial name search { entityId: "<entityId>" } — addons bound to a specific ERM entity { isRequired: true } — mandatory blocks { "errorCount.isActive": true } — addons with retry-branch configured create { payload } — required: label, type. Typically also: options/option (per type), errorCount{...}, expiration{...}, errorMessage. groupId/accountId server-injected. update { id, payload, project? } — any mutable field. Pass a non-empty project to get the updated doc back. delete { id } — soft-delete (status:false). Verify no active Flow references this addon before calling. EXAMPLES list { query: { type: "smart_gpt" }, options: { limit: 20, sort: { createdAt: -1 } } } create { payload: { label: "Classify intent", type: "smart_intent", errorMessage: "Sorry, could you rephrase?", option: { modelId: "<modelId>", categories: [ { category: "billing", type: "queue", queueId: "<qBilling>" }, { category: "sales", type: "block", blockId: "<bSales>" } ] }, errorCount: { isActive: true, limit: 2, jumpType: "queue", queueId: "<fallbackQ>" } } } create { payload: { label: "CSAT 1-5", type: "csat", options: [ { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, { value: 5 } ], isRequired: true } } update { id: "<addonId>", payload: { label: "Classify intent (v2)" } }

smarttalks_agents

ChatGPT
Agent-flow orchestrations (the "v2 agents"): a parent controller + N labelled sub-agents, optionally guarded by a revisor. Used by the GPT flow runtime to classify intent, route to the right domain agent, and post-process the response. INPUT SHAPE { resource, action, ...args } RESOURCES agentConfig — the top-level agent-flow document (controller + sub-agents) agent — one sub-agent nested inside an agentConfig IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment) groupId (ObjectId, REQUIRED, server-injected) accountId (ObjectId, REQUIRED, server-injected) status (boolean, default true) — soft-delete createdAt, updatedAt, createdBy, updatedBy, deletedBy AGENT-CONFIG FIELDS label (string, REQUIRED, trimmed) description (string) controllerConfigs (ModelConfigs) — the classifier prompt that picks which sub-agent to invoke. Default prompt routes by "$agents" expansion to each nested agent's label. revisorConfigs (ModelConfigs) — optional reviewer that rewrites the final response for tone, accuracy, translation. isRevisorActive (boolean, default false) isAudioInterpreterActive (boolean, default false) — enable STT on audio inputs. defaultLanguage (string, default "pt-br") isCrmExtractorActive (boolean, default false) — run CRM field extraction over the transcript. agents[] (nested — see "agent" resource) — the classifier routes to one of these. MODEL-CONFIGS (shape shared by controller, revisor, and sub-agents) prompt (string) — system prompt model (string, default "gpt-4o-mini") apiKey (string) temperature (number), maxTokens (number) examples[] { input (required), output (required) } — few-shot pairs AGENT (nested sub-agent) FIELDS label (string, REQUIRED, trimmed) — used by the controller to identify the intent bucket. description (string) configs (ModelConfigs) — this sub-agent's own prompt + model settings. preprocessingSkills[] (string[]) — Skill ids to run BEFORE the agent (see smarttalks_skills). postprocessingSkills[] (string[]) — Skill ids to run AFTER the agent. RELATIONSHIPS Group 1 ── N AgentConfig.groupId Account 1 ── N AgentConfig.accountId AgentConfig 1 ── N Agent via agents[] Agent N ── M Skill via preprocessingSkills[] / postprocessingSkills[] (smarttalks_skills) Flow.blocks[].gpt.stages[].modelId — GPT blocks may reference skill models orthogonal to this doc. ACTIONS — agentConfig. list { query?, project?, options? } GET /v2/agents — listing endpoint uses a querystring-validated "querySchema" on the upstream; optional args currently not forwarded as filters (safe to send with no args). get { id } GET /v2/agents/:id — full document including nested agents[]. create { payload } POST /v2/agents — Required in payload: label. Optional: description, controllerConfigs, revisorConfigs, isRevisorActive, isAudioInterpreterActive, defaultLanguage, isCrmExtractorActive. Body IS the payload (not wrapped in { payload }). update { id, payload } PATCH /v2/agents/:id — patch mutable fields. Body IS the payload. delete { id } DELETE /v2/agents/:id — soft-delete. ACTIONS — agent. (nested sub-agents) create { id, payload } POST /v2/agents/:id/create — append a sub-agent to agentConfig :id. Required in payload: label. Optional: description, configs, preprocessingSkills[], postprocessingSkills[]. Body IS the payload. get { id, agentId } GET /v2/agents/:id/:agentId — one nested sub-agent. update { id, agentId, payload } PATCH /v2/agents/:id/:agentId — patch a sub-agent. Body IS the payload. delete { id, agentId } DELETE /v2/agents/:id/:agentId — remove a sub-agent from its parent. EXAMPLES list { resource: "agentConfig", action: "list" } get { resource: "agentConfig", action: "get", id: "<configId>" } create { resource: "agentConfig", action: "create", payload: { label: "Support router", isRevisorActive: true } } agent.create { resource: "agent", action: "create", id: "<configId>", payload: { label: "Billing", configs: { prompt: "…", model: "gpt-4o-mini" }, preprocessingSkills: ["<skillId>"] } } agent.update { resource: "agent", action: "update", id: "<configId>", agentId: "<agentId>", payload: { label: "Billing v2" } }

smarttalks_agents_manage

ChatGPT
Agent-flow orchestrations (the "v2 agents"): a parent controller + N labelled sub-agents, optionally guarded by a revisor. Used by the GPT flow runtime to classify intent, route to the right domain agent, and post-process the response. INPUT SHAPE { resource, action, ...args } RESOURCES agentConfig — the top-level agent-flow document (controller + sub-agents) agent — one sub-agent nested inside an agentConfig IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment) groupId (ObjectId, REQUIRED, server-injected) accountId (ObjectId, REQUIRED, server-injected) status (boolean, default true) — soft-delete createdAt, updatedAt, createdBy, updatedBy, deletedBy AGENT-CONFIG FIELDS label (string, REQUIRED, trimmed) description (string) controllerConfigs (ModelConfigs) — the classifier prompt that picks which sub-agent to invoke. Default prompt routes by "$agents" expansion to each nested agent's label. revisorConfigs (ModelConfigs) — optional reviewer that rewrites the final response for tone, accuracy, translation. isRevisorActive (boolean, default false) isAudioInterpreterActive (boolean, default false) — enable STT on audio inputs. defaultLanguage (string, default "pt-br") isCrmExtractorActive (boolean, default false) — run CRM field extraction over the transcript. agents[] (nested — see "agent" resource) — the classifier routes to one of these. MODEL-CONFIGS (shape shared by controller, revisor, and sub-agents) prompt (string) — system prompt model (string, default "gpt-4o-mini") apiKey (string) temperature (number), maxTokens (number) examples[] { input (required), output (required) } — few-shot pairs AGENT (nested sub-agent) FIELDS label (string, REQUIRED, trimmed) — used by the controller to identify the intent bucket. description (string) configs (ModelConfigs) — this sub-agent's own prompt + model settings. preprocessingSkills[] (string[]) — Skill ids to run BEFORE the agent (see smarttalks_skills). postprocessingSkills[] (string[]) — Skill ids to run AFTER the agent. RELATIONSHIPS Group 1 ── N AgentConfig.groupId Account 1 ── N AgentConfig.accountId AgentConfig 1 ── N Agent via agents[] Agent N ── M Skill via preprocessingSkills[] / postprocessingSkills[] (smarttalks_skills) Flow.blocks[].gpt.stages[].modelId — GPT blocks may reference skill models orthogonal to this doc. ACTIONS — agentConfig. list { query?, project?, options? } GET /v2/agents — listing endpoint uses a querystring-validated "querySchema" on the upstream; optional args currently not forwarded as filters (safe to send with no args). get { id } GET /v2/agents/:id — full document including nested agents[]. create { payload } POST /v2/agents — Required in payload: label. Optional: description, controllerConfigs, revisorConfigs, isRevisorActive, isAudioInterpreterActive, defaultLanguage, isCrmExtractorActive. Body IS the payload (not wrapped in { payload }). update { id, payload } PATCH /v2/agents/:id — patch mutable fields. Body IS the payload. delete { id } DELETE /v2/agents/:id — soft-delete. ACTIONS — agent. (nested sub-agents) create { id, payload } POST /v2/agents/:id/create — append a sub-agent to agentConfig :id. Required in payload: label. Optional: description, configs, preprocessingSkills[], postprocessingSkills[]. Body IS the payload. get { id, agentId } GET /v2/agents/:id/:agentId — one nested sub-agent. update { id, agentId, payload } PATCH /v2/agents/:id/:agentId — patch a sub-agent. Body IS the payload. delete { id, agentId } DELETE /v2/agents/:id/:agentId — remove a sub-agent from its parent. EXAMPLES list { resource: "agentConfig", action: "list" } get { resource: "agentConfig", action: "get", id: "<configId>" } create { resource: "agentConfig", action: "create", payload: { label: "Support router", isRevisorActive: true } } agent.create { resource: "agent", action: "create", id: "<configId>", payload: { label: "Billing", configs: { prompt: "…", model: "gpt-4o-mini" }, preprocessingSkills: ["<skillId>"] } } agent.update { resource: "agent", action: "update", id: "<configId>", agentId: "<agentId>", payload: { label: "Billing v2" } }

smarttalks_appointments

ChatGPT
Appointments — individual BOOKINGS made against a schedule's availability slots. Each appointment belongs to exactly one Schedule and one CRM contact (clientId). Scoped to Group + Account; every read/write is auto-scoped to the caller's JWT claims. Mounted under /v1/schedules/:scheduleId/appointments. INPUT SHAPE { resource: "appointment", action, scheduleId, ...args } scheduleId is REQUIRED for every action. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, server-injected) — parent tenant Group accountId (ObjectId, server-injected) — parent Account units (ObjectId) — org unit scope scheduleId (ObjectId, ref Schedule) — parent schedule (always required) clientId (ObjectId, required, ref Item/CRM Person) — the booked contact createdBy, updatedBy, deletedBy (ObjectId) — audit trail createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null) — soft-delete; null = active, present = trashed BOOKING CORE ticket (string, required, unique per group+account) — human-readable booking reference scheduleType (enum, required, mirrors parent Schedule.type): "REGULAR" | "RENTAL" | "TABLE" date (Date, required) — calendar date of the appointment startTime, endTime (Date, required) — exact time bounds of the slot duration (number, required) — slot duration in minutes amount (number, default 1) — number of people in the booking observation (string) — free-text notes from the booker STATUS LIFECYCLE status (enum): "PENDING" | "CONFIRMED" | "CANCELLED" | "RESCHEDULED" | "REJECTED" | "SCHEDULED" | "NO_SHOW" | "SHOW" Transitions: PENDING → CONFIRMED | REJECTED; CONFIRMED | SCHEDULED → CANCELLED | NO_SHOW | SHOW | RESCHEDULED cancellationReason (string) — set when status=CANCELLED rejectionReason (string) — set when status=REJECTED rescheduled (boolean) — true when this appointment was moved (status=RESCHEDULED) hasConflict (boolean, default false) — set by the server when a Conflict record exists for this appointment RENTAL ITEMS (RENTAL schedules only) items[] — [{ itemId (string, rental_item.id), quantity (number), totalPrice (number) }] Server validates inventory against Schedule.rental_items quantities. TABLE SEATS (TABLE schedules only) tables[] — [{ tableId (string, table.id), seatsRequired (number) }] RECURRENCE isRecurring (boolean, default false) — part of a recurring series recurrenceGroupId (string | null) — groups all appointments in a recurring series (nanoid) recurrenceIndex (number | null) — position within the series (0-based) recurrenceTotalCount (number | null) — total planned occurrences in the series recurrenceFrequency (enum): "weekly" | "biweekly" | "monthly" UTM TRACKING utm.source, utm.medium, utm.campaign, utm.term, utm.content (string) — tracking params from public booking page CUSTOM DATA customData (object) — arbitrary JSON payload; validated against Schedule.conditions when present INDEXES (shape queries to hit these) (groupId, accountId, units, createdAt) (groupId, accountId, createdAt) (groupId, accountId, ticket, status) — unique composite (groupId, accountId, deletedAt, startTime, scheduleId) — range queries (scheduleId, recurrenceGroupId) — recurrence group lookup Always include scheduleId + group/account context. RELATIONSHIPS Schedule 1 ── N Appointment.scheduleId CRMPerson 1 ── N Appointment.clientId Conflict 1 ── N Appointment (via Conflict.appointmentId) RecurrenceGroup: Appointment N ── 1 (recurrenceGroupId string) ACTIONS appointment.list { scheduleId, search?, orderBy?, order?, skip?, limit?, trash?, date?, startDate?, endDate?, recurrenceGroupId?, isRecurring? } Lists appointments within a schedule. search — matches ticket, observation, status, utm fields, id, _id. date — filter by single day (applies gte/lte on appointment.date). startDate / endDate — date range filter on appointment.date (ISO strings). recurrenceGroupId — filter by recurrence series. isRecurring — true/false filter on isRecurring field. trash: true — trashed appointments; false (default) — active only. Returns { items: Appointment[], count: number }. appointment.get { scheduleId, id } Fetch one appointment by ObjectId. appointment.create { scheduleId, payload } Create a single appointment. payload required fields: clientId, date, startTime, duration. Server validates slot availability, capacity, and period window. Server auto-generates: ticket (nanoid), id (counter), timestamps. appointment.update { scheduleId, id, payload } Patch any mutable field. Partial update — only include changed keys. appointment.updateStatus { scheduleId, id, status, cancellationReason?, rejectionReason? } PATCH /:scheduleId/appointments/:id/status — lightweight status transition. status enum: "PENDING"|"CONFIRMED"|"CANCELLED"|"REJECTED"|"SCHEDULED"|"NO_SHOW"|"SHOW" Use cancellationReason when transitioning to CANCELLED. Use rejectionReason when transitioning to REJECTED. Returns 204 (no body on success). appointment.delete { scheduleId, id } Soft-delete (sets deletedAt to now). Returns 204. appointment.previewRecurrence { scheduleId, date, startTime, duration, frequency, totalCount, amount?, items? } POST /:scheduleId/appointments/preview-recurrence — simulate a recurring series without booking. date — ISO date of the first occurrence. startTime — ISO datetime preserving time-of-day across all occurrences. duration — slot duration in minutes. frequency — "weekly" | "biweekly" | "monthly". totalCount — number of occurrences (2–52). amount — people count per occurrence (default 1). items — rental item reservations for RENTAL schedules. Returns array of { date, startTime, available: boolean, reason? } for each occurrence. reason values: "outside_period" | "exception_date" | "no_availability" | "slot_full" | "item_unavailable" appointment.createRecurring { scheduleId, payload } POST /:scheduleId/appointments/recurring — create an entire recurring series in one call. payload shape mirrors previewRecurrence body. Server applies same availability checks. Returns 201 with the created appointments array. appointment.cancelRecurrenceGroup { scheduleId, recurrenceGroupId, fromIndex? } DELETE /:scheduleId/recurrence-groups/:recurrenceGroupId — bulk-cancel future appointments in a series. fromIndex — cancel only occurrences with recurrenceIndex >= fromIndex; omit to cancel all future. Only appointments with non-final statuses (not CANCELLED|SHOW|NO_SHOW|REJECTED) and startTime >= now are affected. Returns { cancelledCount: number }. EXAMPLES appointment.list { resource: "appointment", action: "list", scheduleId: "<sid>", limit: 20 } appointment.get { resource: "appointment", action: "get", scheduleId: "<sid>", id: "<aid>" } appointment.create { resource: "appointment", action: "create", scheduleId: "<sid>", payload: { clientId: "<cid>", date: "2026-05-10", startTime: "2026-05-10T09:00:00Z", duration: 60 } } appointment.updateStatus { resource: "appointment", action: "updateStatus", scheduleId: "<sid>", id: "<aid>", status: "CONFIRMED" } appointment.previewRecurrence { resource: "appointment", action: "previewRecurrence", scheduleId: "<sid>", date: "2026-05-10", startTime: "2026-05-10T09:00:00Z", duration: 60, frequency: "weekly", totalCount: 4 } appointment.cancelRecurrenceGroup { resource: "appointment", action: "cancelRecurrenceGroup", scheduleId: "<sid>", recurrenceGroupId: "<rgid>", fromIndex: 2 }

smarttalks_appointments_manage

ChatGPT
Appointments — individual BOOKINGS made against a schedule's availability slots. Each appointment belongs to exactly one Schedule and one CRM contact (clientId). Scoped to Group + Account; every read/write is auto-scoped to the caller's JWT claims. Mounted under /v1/schedules/:scheduleId/appointments. INPUT SHAPE { resource: "appointment", action, scheduleId, ...args } scheduleId is REQUIRED for every action. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, server-injected) — parent tenant Group accountId (ObjectId, server-injected) — parent Account units (ObjectId) — org unit scope scheduleId (ObjectId, ref Schedule) — parent schedule (always required) clientId (ObjectId, required, ref Item/CRM Person) — the booked contact createdBy, updatedBy, deletedBy (ObjectId) — audit trail createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null) — soft-delete; null = active, present = trashed BOOKING CORE ticket (string, required, unique per group+account) — human-readable booking reference scheduleType (enum, required, mirrors parent Schedule.type): "REGULAR" | "RENTAL" | "TABLE" date (Date, required) — calendar date of the appointment startTime, endTime (Date, required) — exact time bounds of the slot duration (number, required) — slot duration in minutes amount (number, default 1) — number of people in the booking observation (string) — free-text notes from the booker STATUS LIFECYCLE status (enum): "PENDING" | "CONFIRMED" | "CANCELLED" | "RESCHEDULED" | "REJECTED" | "SCHEDULED" | "NO_SHOW" | "SHOW" Transitions: PENDING → CONFIRMED | REJECTED; CONFIRMED | SCHEDULED → CANCELLED | NO_SHOW | SHOW | RESCHEDULED cancellationReason (string) — set when status=CANCELLED rejectionReason (string) — set when status=REJECTED rescheduled (boolean) — true when this appointment was moved (status=RESCHEDULED) hasConflict (boolean, default false) — set by the server when a Conflict record exists for this appointment RENTAL ITEMS (RENTAL schedules only) items[] — [{ itemId (string, rental_item.id), quantity (number), totalPrice (number) }] Server validates inventory against Schedule.rental_items quantities. TABLE SEATS (TABLE schedules only) tables[] — [{ tableId (string, table.id), seatsRequired (number) }] RECURRENCE isRecurring (boolean, default false) — part of a recurring series recurrenceGroupId (string | null) — groups all appointments in a recurring series (nanoid) recurrenceIndex (number | null) — position within the series (0-based) recurrenceTotalCount (number | null) — total planned occurrences in the series recurrenceFrequency (enum): "weekly" | "biweekly" | "monthly" UTM TRACKING utm.source, utm.medium, utm.campaign, utm.term, utm.content (string) — tracking params from public booking page CUSTOM DATA customData (object) — arbitrary JSON payload; validated against Schedule.conditions when present INDEXES (shape queries to hit these) (groupId, accountId, units, createdAt) (groupId, accountId, createdAt) (groupId, accountId, ticket, status) — unique composite (groupId, accountId, deletedAt, startTime, scheduleId) — range queries (scheduleId, recurrenceGroupId) — recurrence group lookup Always include scheduleId + group/account context. RELATIONSHIPS Schedule 1 ── N Appointment.scheduleId CRMPerson 1 ── N Appointment.clientId Conflict 1 ── N Appointment (via Conflict.appointmentId) RecurrenceGroup: Appointment N ── 1 (recurrenceGroupId string) ACTIONS appointment.list { scheduleId, search?, orderBy?, order?, skip?, limit?, trash?, date?, startDate?, endDate?, recurrenceGroupId?, isRecurring? } Lists appointments within a schedule. search — matches ticket, observation, status, utm fields, id, _id. date — filter by single day (applies gte/lte on appointment.date). startDate / endDate — date range filter on appointment.date (ISO strings). recurrenceGroupId — filter by recurrence series. isRecurring — true/false filter on isRecurring field. trash: true — trashed appointments; false (default) — active only. Returns { items: Appointment[], count: number }. appointment.get { scheduleId, id } Fetch one appointment by ObjectId. appointment.create { scheduleId, payload } Create a single appointment. payload required fields: clientId, date, startTime, duration. Server validates slot availability, capacity, and period window. Server auto-generates: ticket (nanoid), id (counter), timestamps. appointment.update { scheduleId, id, payload } Patch any mutable field. Partial update — only include changed keys. appointment.updateStatus { scheduleId, id, status, cancellationReason?, rejectionReason? } PATCH /:scheduleId/appointments/:id/status — lightweight status transition. status enum: "PENDING"|"CONFIRMED"|"CANCELLED"|"REJECTED"|"SCHEDULED"|"NO_SHOW"|"SHOW" Use cancellationReason when transitioning to CANCELLED. Use rejectionReason when transitioning to REJECTED. Returns 204 (no body on success). appointment.delete { scheduleId, id } Soft-delete (sets deletedAt to now). Returns 204. appointment.previewRecurrence { scheduleId, date, startTime, duration, frequency, totalCount, amount?, items? } POST /:scheduleId/appointments/preview-recurrence — simulate a recurring series without booking. date — ISO date of the first occurrence. startTime — ISO datetime preserving time-of-day across all occurrences. duration — slot duration in minutes. frequency — "weekly" | "biweekly" | "monthly". totalCount — number of occurrences (2–52). amount — people count per occurrence (default 1). items — rental item reservations for RENTAL schedules. Returns array of { date, startTime, available: boolean, reason? } for each occurrence. reason values: "outside_period" | "exception_date" | "no_availability" | "slot_full" | "item_unavailable" appointment.createRecurring { scheduleId, payload } POST /:scheduleId/appointments/recurring — create an entire recurring series in one call. payload shape mirrors previewRecurrence body. Server applies same availability checks. Returns 201 with the created appointments array. appointment.cancelRecurrenceGroup { scheduleId, recurrenceGroupId, fromIndex? } DELETE /:scheduleId/recurrence-groups/:recurrenceGroupId — bulk-cancel future appointments in a series. fromIndex — cancel only occurrences with recurrenceIndex >= fromIndex; omit to cancel all future. Only appointments with non-final statuses (not CANCELLED|SHOW|NO_SHOW|REJECTED) and startTime >= now are affected. Returns { cancelledCount: number }. EXAMPLES appointment.list { resource: "appointment", action: "list", scheduleId: "<sid>", limit: 20 } appointment.get { resource: "appointment", action: "get", scheduleId: "<sid>", id: "<aid>" } appointment.create { resource: "appointment", action: "create", scheduleId: "<sid>", payload: { clientId: "<cid>", date: "2026-05-10", startTime: "2026-05-10T09:00:00Z", duration: 60 } } appointment.updateStatus { resource: "appointment", action: "updateStatus", scheduleId: "<sid>", id: "<aid>", status: "CONFIRMED" } appointment.previewRecurrence { resource: "appointment", action: "previewRecurrence", scheduleId: "<sid>", date: "2026-05-10", startTime: "2026-05-10T09:00:00Z", duration: 60, frequency: "weekly", totalCount: 4 } appointment.cancelRecurrenceGroup { resource: "appointment", action: "cancelRecurrenceGroup", scheduleId: "<sid>", recurrenceGroupId: "<rgid>", fromIndex: 2 }

smarttalks_campaigns

ChatGPT
Campaigns — NOTIFICATION / BROADCAST CAMPAIGNS (WhatsApp HSM, email blasts, in-flow drops). A Campaign coordinates a template + an audience + a schedule against a single Channel, tracked by an append-only stats[] array. INPUT SHAPE { action, ...args } IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment) groupId (ObjectId, required, server-injected) accountId (ObjectId, required, server-injected) status (boolean, default true) — soft-delete; pre-find middleware filters status:true createdAt, updatedAt, createdBy, updatedBy, deletedBy CORE FIELDS title (string, REQUIRED) description (string) body (string) — message body / HTML for email campaigns channelId (ObjectId, REQUIRED) — the Channel the campaign sends through templateId (ObjectId, ref Templates) — WhatsApp HSM template, when applicable filterId, filterType (ObjectId/string) — references a saved Hermes filter that resolves the audience lists[] (ObjectId[]), skipLists[] (ObjectId[]) — explicit include / exclude lists timezone (string, default "America/Sao_Paulo") isActive (boolean, default false) — campaign is currently enabled sendToActiveInteractions (boolean), sendToHumanizedService (boolean) attachment { url, type } jobId (ObjectId) — BullMQ job handle for recurrent campaigns SCHEDULE startSendDate (Date, default now), endSendDate (Date) neverEnd (boolean), sendTime (string), repeatInterval (number) repeatCategory (enum): "day" | "week" | "month" frequency (enum, default "one_shot"): "scheduled" | "recurrent" | "one_shot" recurrence { sunday, monday, tuesday, wednesday, thursday, friday, saturday } — per-day booleans for weekly recurrence CLASSIFICATION & STATE type (enum, default "broadcast"): "broadcast" | "notifications" processStatus (enum, default "scheduled"): "scheduled" | "processing" | "done" | "error" | "paused" TEMPLATE PARAMETERS parameters[] — HSM variable bindings for rendering the template: { variable, field (required), value, type (default "text"), component (enum "BODY"|"HEADER"|"FOOTER"|"BUTTONS", default "BODY"), link } REDIRECT (post-delivery routing) redirectTo { flowId (ObjectId), blockId (ObjectId), contextIndex (number, default 0), sendToAttendance (boolean), queueId (ObjectId) } STATS (append-only — one entry per delivery wave) stats[] { possibleContacts (number), deliveryDate (Date), validContacts[], invalidContacts[], totalSent (number), totalUnsent (number), impactedContacts[], unImpactedContacts[], openWindow[], windowClosed[], completed (boolean), finishedAt (Date), totalResponseBefore, totalResponseAfter, totalRead, totalDelivered, templateIsPaused (boolean), stackTrace (string), errorMessage (string) } RELATIONSHIPS Group 1 ── N Campaign.groupId Account 1 ── N Campaign.accountId Channel 1 ── N Campaign.channelId (REQUIRED — campaign cannot exist without a channel) Template 1 ── N Campaign.templateId (optional, HSM campaigns) Filter → referenced via filterId+filterType; audience resolved at send time Flow/Block/Queue → referenced via redirectTo for post-delivery routing ACTIONS list { query?, project?, options? } POST /v1/campaigns — Mongo-style read (server handles auth scoping). Common: { type: "broadcast" } — by type { processStatus: "processing" } — currently running { channelId: "<id>" } — campaigns on one channel { templateId: "<id>" } — campaigns using one template options supports { limit, skip, sort }. get { id } GET /v1/campaigns/:id — one campaign (full document including stats[]). create { payload } PUT /v1/campaigns — Required in payload: title, channelId. Optional: templateId, parameters[], redirectTo, lists[], skipLists[], filterId+filterType, schedule fields (startSendDate, endSendDate, frequency, recurrence), type, attachment, timezone. groupId / accountId / id / jobId are server-managed. update { id, payload, project? } PATCH /v1/campaigns/:id — patch mutable fields. processStatus/stats are typically server-owned. delete { id } DELETE /v1/campaigns/:id — soft-delete (status:false). updateBody { id, payload } PATCH /v1/campaigns/:id/body — dedicated endpoint for editing the rich body (used by email & broadcast editors). activate GATED OFF — set SMART_MCP_ALLOW_VOYAGER_SIDE_EFFECTS=true to enable. Toggles live delivery. stats { id } GET /v1/campaigns/:id/stats — roll-up statistics for one campaign (aggregated from stats[] + deliveries). options { query?, options? } POST /v1/campaigns/options — list of { value, label } options for campaigns that delivered in a date range. query MUST include startDate + endDate (ISO strings). Used by UI pickers. export { startDate?, endDate?, campaignIds?, fields? } GET /v1/campaigns/export?startDate=…&endDate=…&campaignIds=a&campaignIds=b&fields=title Aggregated CSV-ish roll-up of notification campaigns in a date range. Response may be CSV text rather than JSON. EXAMPLES list { query: { type: "broadcast", processStatus: "done" }, options: { limit: 20, sort: { createdAt: -1 } } } get { id: "<campaignId>" } create { payload: { title: "April blast", channelId: "<chId>", templateId: "<tmplId>", startSendDate: "2026-04-25T09:00:00Z", frequency: "one_shot" } } update { id: "<campaignId>", payload: { description: "Updated" } } stats { id: "<campaignId>" } options { query: { startDate: "2026-01-01", endDate: "2026-01-31" } } export { startDate: "2026-01-01", endDate: "2026-01-31", campaignIds: ["<c1>", "<c2>"], fields: ["title","deliveryDate"] }

smarttalks_campaigns_manage

ChatGPT
Campaigns — NOTIFICATION / BROADCAST CAMPAIGNS (WhatsApp HSM, email blasts, in-flow drops). A Campaign coordinates a template + an audience + a schedule against a single Channel, tracked by an append-only stats[] array. INPUT SHAPE { action, ...args } IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment) groupId (ObjectId, required, server-injected) accountId (ObjectId, required, server-injected) status (boolean, default true) — soft-delete; pre-find middleware filters status:true createdAt, updatedAt, createdBy, updatedBy, deletedBy CORE FIELDS title (string, REQUIRED) description (string) body (string) — message body / HTML for email campaigns channelId (ObjectId, REQUIRED) — the Channel the campaign sends through templateId (ObjectId, ref Templates) — WhatsApp HSM template, when applicable filterId, filterType (ObjectId/string) — references a saved Hermes filter that resolves the audience lists[] (ObjectId[]), skipLists[] (ObjectId[]) — explicit include / exclude lists timezone (string, default "America/Sao_Paulo") isActive (boolean, default false) — campaign is currently enabled sendToActiveInteractions (boolean), sendToHumanizedService (boolean) attachment { url, type } jobId (ObjectId) — BullMQ job handle for recurrent campaigns SCHEDULE startSendDate (Date, default now), endSendDate (Date) neverEnd (boolean), sendTime (string), repeatInterval (number) repeatCategory (enum): "day" | "week" | "month" frequency (enum, default "one_shot"): "scheduled" | "recurrent" | "one_shot" recurrence { sunday, monday, tuesday, wednesday, thursday, friday, saturday } — per-day booleans for weekly recurrence CLASSIFICATION & STATE type (enum, default "broadcast"): "broadcast" | "notifications" processStatus (enum, default "scheduled"): "scheduled" | "processing" | "done" | "error" | "paused" TEMPLATE PARAMETERS parameters[] — HSM variable bindings for rendering the template: { variable, field (required), value, type (default "text"), component (enum "BODY"|"HEADER"|"FOOTER"|"BUTTONS", default "BODY"), link } REDIRECT (post-delivery routing) redirectTo { flowId (ObjectId), blockId (ObjectId), contextIndex (number, default 0), sendToAttendance (boolean), queueId (ObjectId) } STATS (append-only — one entry per delivery wave) stats[] { possibleContacts (number), deliveryDate (Date), validContacts[], invalidContacts[], totalSent (number), totalUnsent (number), impactedContacts[], unImpactedContacts[], openWindow[], windowClosed[], completed (boolean), finishedAt (Date), totalResponseBefore, totalResponseAfter, totalRead, totalDelivered, templateIsPaused (boolean), stackTrace (string), errorMessage (string) } RELATIONSHIPS Group 1 ── N Campaign.groupId Account 1 ── N Campaign.accountId Channel 1 ── N Campaign.channelId (REQUIRED — campaign cannot exist without a channel) Template 1 ── N Campaign.templateId (optional, HSM campaigns) Filter → referenced via filterId+filterType; audience resolved at send time Flow/Block/Queue → referenced via redirectTo for post-delivery routing ACTIONS list { query?, project?, options? } POST /v1/campaigns — Mongo-style read (server handles auth scoping). Common: { type: "broadcast" } — by type { processStatus: "processing" } — currently running { channelId: "<id>" } — campaigns on one channel { templateId: "<id>" } — campaigns using one template options supports { limit, skip, sort }. get { id } GET /v1/campaigns/:id — one campaign (full document including stats[]). create { payload } PUT /v1/campaigns — Required in payload: title, channelId. Optional: templateId, parameters[], redirectTo, lists[], skipLists[], filterId+filterType, schedule fields (startSendDate, endSendDate, frequency, recurrence), type, attachment, timezone. groupId / accountId / id / jobId are server-managed. update { id, payload, project? } PATCH /v1/campaigns/:id — patch mutable fields. processStatus/stats are typically server-owned. delete { id } DELETE /v1/campaigns/:id — soft-delete (status:false). updateBody { id, payload } PATCH /v1/campaigns/:id/body — dedicated endpoint for editing the rich body (used by email & broadcast editors). activate GATED OFF — set SMART_MCP_ALLOW_VOYAGER_SIDE_EFFECTS=true to enable. Toggles live delivery. stats { id } GET /v1/campaigns/:id/stats — roll-up statistics for one campaign (aggregated from stats[] + deliveries). options { query?, options? } POST /v1/campaigns/options — list of { value, label } options for campaigns that delivered in a date range. query MUST include startDate + endDate (ISO strings). Used by UI pickers. export { startDate?, endDate?, campaignIds?, fields? } GET /v1/campaigns/export?startDate=…&endDate=…&campaignIds=a&campaignIds=b&fields=title Aggregated CSV-ish roll-up of notification campaigns in a date range. Response may be CSV text rather than JSON. EXAMPLES list { query: { type: "broadcast", processStatus: "done" }, options: { limit: 20, sort: { createdAt: -1 } } } get { id: "<campaignId>" } create { payload: { title: "April blast", channelId: "<chId>", templateId: "<tmplId>", startSendDate: "2026-04-25T09:00:00Z", frequency: "one_shot" } } update { id: "<campaignId>", payload: { description: "Updated" } } stats { id: "<campaignId>" } options { query: { startDate: "2026-01-01", endDate: "2026-01-31" } } export { startDate: "2026-01-01", endDate: "2026-01-31", campaignIds: ["<c1>", "<c2>"], fields: ["title","deliveryDate"] }

smarttalks_channels

ChatGPT
Channels — MESSAGING CHANNEL CONFIGS (WhatsApp, Messenger, Instagram, Widget, Email, NVoip, …). A Channel is the credentialed endpoint a Flow/Campaign binds to for inbound/outbound traffic. Scoped to one Group + Account. INPUT SHAPE { action, ...args } IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment) groupId (ObjectId, REQUIRED, server-injected) accountId (ObjectId, REQUIRED, server-injected) status (boolean, default true) — soft-delete createdAt, updatedAt, createdBy, updatedBy, deletedBy CORE FIELDS label (string, REQUIRED, trimmed) caption (string) type (enum, REQUIRED): "messenger" | "dialog360" | "instagram" | "widget" | "whatsapp" | "gupshup" | "soulmachines" | "whatsappweb" | "whatsappevo" | "email" | "nvoip" isActive (boolean, default false) providerKey (string, unique UUID, auto-assigned) — stable external identifier used for webhook routes apiKey (string) — bearer token / API key for the upstream provider wabaId, wabaPhone, phoneNumber, webhook (string) — telephony / meta identifiers flowId (ObjectId) — default Flow entry-point for inbound events units[] (ObjectId[]) — Units this channel is scoped to PROVIDER-SPECIFIC NESTED CONFIG dialog360 { clientId, channelId, isMigrated } gupshup { appName, appId, wabaPhone } messenger { pageId, botName, pageAccessToken } instagram { pageId, botName, appKey, appId, eventFilters{...} } widgetConfigs { … UI/appearance settings } — WIDGET channel only emailConfig { … Mailgun route/domain/auth data } — EMAIL channel only nvoipConfig { … NVoip credentials } — NVOIP channel only whatsappevo { … Evolution API settings } MENUS (array) menus[] { flowId (ObjectId), blockId (ObjectId), ... } — deep-link menu entries that route to specific flow/block combos. RELATIONSHIPS Group 1 ── N Channel.groupId Account 1 ── N Channel.accountId Channel 1 ── N Campaign.channelId (a channel anchors many campaigns) Channel 1 ── N Flow (via flowId / menus[]) — the default routing flow Channel N ── M Units via units[] ACTIONS list { query?, project?, options? } POST /v1/channels — Mongo-style read. Common: { type: "whatsapp" } — by channel type { isActive: true } — enabled channels { "units": "<unitId>" } — scoped to a unit get { id } GET /v1/channels/:id — one channel including provider-specific nested config. create / update / delete GATED OFF — set SMART_MCP_ALLOW_VOYAGER_SIDE_EFFECTS=true to enable. All three mutate live messaging infrastructure. When enabled, create/update restrict payload.type to "email" or "widget". options { query?, options? } POST /v1/channels/options — simplified {value,label,type} list for UI pickers; body is typically empty. validateWebhook { payload } POST /v1/channels/validate-webhook — probe an upstream webhook URL and report { exists }. payload shape mirrors Channels.validateWebhook (e.g. { webhook, type }). emailDomain { providerKey } GET /v1/channels/:providerKey/email/domain — current Mailgun domain + DNS records for an email channel. emailDomainVerify { providerKey } GET /v1/channels/:providerKey/email/domain-verify — re-run domain verification and return the result. EXAMPLES list { query: { type: "whatsapp", isActive: true } } get { id: "<channelId>" } validateWebhook { payload: { webhook: "https://…", type: "whatsapp" } } emailDomain { providerKey: "<uuid>" } emailDomainVerify { providerKey: "<uuid>" }

smarttalks_clients

ChatGPT
Clients — Companies/Organizations with billing contracts. ACTIONS list { search?, status?, groupId?, page?, limit? } — List all clients with optional filters get { id } — Fetch one client by ID create { payload } — Create new client. Required: name, legalName, cnpj, contacts[] contacts[0] must have email and phone fields update { id, payload } — Update client fields. _id and contaAzulCustomerId are protected delete { id } — Soft-delete client (marks as deleted) stats { } — Get count statistics broken down by status export_conta_azul { id } — Push client to Conta Azul API. Auto-links if already registered link_conta_azul { id, contaAzulCustomerId } — Manual link to existing Conta Azul customer link_by_document { id } — Search Conta Azul by CNPJ and auto-link the customer

smarttalks_clients_manage

ChatGPT
Clients — Companies/Organizations with billing contracts. ACTIONS list { search?, status?, groupId?, page?, limit? } — List all clients with optional filters get { id } — Fetch one client by ID create { payload } — Create new client. Required: name, legalName, cnpj, contacts[] contacts[0] must have email and phone fields update { id, payload } — Update client fields. _id and contaAzulCustomerId are protected delete { id } — Soft-delete client (marks as deleted) stats { } — Get count statistics broken down by status export_conta_azul { id } — Push client to Conta Azul API. Auto-links if already registered link_conta_azul { id, contaAzulCustomerId } — Manual link to existing Conta Azul customer link_by_document { id } — Search Conta Azul by CNPJ and auto-link the customer

smarttalks_conflicts

ChatGPT
Scheduling conflicts — SCHEDULING CONFLICTS detected when a previously valid appointment becomes invalid due to schedule changes (availability removed, schedule deactivated, capacity exceeded, or exception added). Scoped to Group + Account; every read/write is auto-scoped to the caller's JWT claims. Mounted under /v1/schedules/conflicts. INPUT SHAPE { resource: "conflict", action, ...args } IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically CONFLICT RECORD scheduleId (string, required) — ObjectId of the affected schedule scheduleTitle (string) — snapshot of the schedule title at detection time appointmentId (string, required) — ObjectId of the affected appointment clientId (string) — ObjectId of the booked contact appointmentDate (Date) — date of the conflicted appointment appointmentStartTime (Date) — start time of the conflicted appointment appointmentStatus (string) — appointment status at detection time CONFLICT TYPE type (enum, required): describes why the conflict was detected "exception_added" — an exception date was added to the schedule covering the appointment's date "capacity_exceeded" — schedule capacity was reduced below existing bookings "schedule_deactivated" — the parent schedule was deactivated while appointments existed "availability_removed" — the availability window that covered the slot was removed CONFLICT STATUS & RESOLUTION status (enum, default "active"): "active" | "resolved" detectedAt (Date, default now) — when the conflict was created resolvedAt (Date | null, default null) — when the conflict was resolved resolvedBy (string | null, default null) — ObjectId of the user who resolved it resolution (enum | null, default null): "manual" | "cancelled" | "rescheduled" | "auto" "manual" — operator acknowledged without rescheduling or cancelling "cancelled" — the appointment was cancelled to resolve the conflict "rescheduled" — the appointment was moved to a new slot "auto" — resolved automatically by the system reason (string) — free-text description of the conflict SIDE EFFECTS ON RESOLVE When the last active conflict for an appointment is resolved, the server automatically sets Appointment.hasConflict = false. INDEXES (shape queries to hit these) (scheduleId, status) — filter by schedule + status (appointmentId) — look up all conflicts for a specific appointment RELATIONSHIPS Schedule 1 ── N Conflict.scheduleId Appointment 1 ── N Conflict.appointmentId Conflict.resolvedBy → User._id ACTIONS conflict.list { status?, skip?, limit? } GET /v1/schedules/conflicts — list conflicts for the caller's account/group. status — filter by "active" or "resolved". Omit to return all. skip (default 0), limit (default 50) — pagination. Sorted by detectedAt descending (most recent first). Returns { items: Conflict[], count: number }. conflict.resolve { id, status: "resolved", resolution } PUT /v1/schedules/conflicts/:id — resolve an active conflict. id — ObjectId of the conflict to resolve. status — must be "resolved" (the only allowed transition). resolution — how it was handled: "manual" | "cancelled" | "rescheduled" Server sets resolvedAt and resolvedBy from the caller's JWT. If no remaining active conflicts exist for the appointment, Appointment.hasConflict is cleared. Returns the updated Conflict document. EXAMPLES conflict.list { resource: "conflict", action: "list", status: "active", limit: 20 } conflict.list { resource: "conflict", action: "list" } conflict.resolve { resource: "conflict", action: "resolve", id: "<conflictId>", status: "resolved", resolution: "cancelled" } conflict.resolve { resource: "conflict", action: "resolve", id: "<conflictId>", status: "resolved", resolution: "manual" }

smarttalks_conflicts_manage

ChatGPT
Scheduling conflicts — SCHEDULING CONFLICTS detected when a previously valid appointment becomes invalid due to schedule changes (availability removed, schedule deactivated, capacity exceeded, or exception added). Scoped to Group + Account; every read/write is auto-scoped to the caller's JWT claims. Mounted under /v1/schedules/conflicts. INPUT SHAPE { resource: "conflict", action, ...args } IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically CONFLICT RECORD scheduleId (string, required) — ObjectId of the affected schedule scheduleTitle (string) — snapshot of the schedule title at detection time appointmentId (string, required) — ObjectId of the affected appointment clientId (string) — ObjectId of the booked contact appointmentDate (Date) — date of the conflicted appointment appointmentStartTime (Date) — start time of the conflicted appointment appointmentStatus (string) — appointment status at detection time CONFLICT TYPE type (enum, required): describes why the conflict was detected "exception_added" — an exception date was added to the schedule covering the appointment's date "capacity_exceeded" — schedule capacity was reduced below existing bookings "schedule_deactivated" — the parent schedule was deactivated while appointments existed "availability_removed" — the availability window that covered the slot was removed CONFLICT STATUS & RESOLUTION status (enum, default "active"): "active" | "resolved" detectedAt (Date, default now) — when the conflict was created resolvedAt (Date | null, default null) — when the conflict was resolved resolvedBy (string | null, default null) — ObjectId of the user who resolved it resolution (enum | null, default null): "manual" | "cancelled" | "rescheduled" | "auto" "manual" — operator acknowledged without rescheduling or cancelling "cancelled" — the appointment was cancelled to resolve the conflict "rescheduled" — the appointment was moved to a new slot "auto" — resolved automatically by the system reason (string) — free-text description of the conflict SIDE EFFECTS ON RESOLVE When the last active conflict for an appointment is resolved, the server automatically sets Appointment.hasConflict = false. INDEXES (shape queries to hit these) (scheduleId, status) — filter by schedule + status (appointmentId) — look up all conflicts for a specific appointment RELATIONSHIPS Schedule 1 ── N Conflict.scheduleId Appointment 1 ── N Conflict.appointmentId Conflict.resolvedBy → User._id ACTIONS conflict.list { status?, skip?, limit? } GET /v1/schedules/conflicts — list conflicts for the caller's account/group. status — filter by "active" or "resolved". Omit to return all. skip (default 0), limit (default 50) — pagination. Sorted by detectedAt descending (most recent first). Returns { items: Conflict[], count: number }. conflict.resolve { id, status: "resolved", resolution } PUT /v1/schedules/conflicts/:id — resolve an active conflict. id — ObjectId of the conflict to resolve. status — must be "resolved" (the only allowed transition). resolution — how it was handled: "manual" | "cancelled" | "rescheduled" Server sets resolvedAt and resolvedBy from the caller's JWT. If no remaining active conflicts exist for the appointment, Appointment.hasConflict is cleared. Returns the updated Conflict document. EXAMPLES conflict.list { resource: "conflict", action: "list", status: "active", limit: 20 } conflict.list { resource: "conflict", action: "list" } conflict.resolve { resource: "conflict", action: "resolve", id: "<conflictId>", status: "resolved", resolution: "cancelled" } conflict.resolve { resource: "conflict", action: "resolve", id: "<conflictId>", status: "resolved", resolution: "manual" }

smarttalks_connectors

ChatGPT
Connectors — REUSABLE OUTBOUND HTTP CALL DEFINITIONS. A connector is a named, tenant-scoped HTTP request template (method + url + headers + data) with per-flow template substitution, used by Flows/Addons/Agents to integrate with external systems. At runtime, Engine resolves template placeholders against the active flow context, issues the request via axios, and PERSISTS the raw response body under refs.connectors[<connectorId>] — both in memory for downstream template lookups and on the interaction document (field connectors.<connectorId>) — so later flow steps can reference the result via the DSL. Tenancy is enforced server-side. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters to status:true createdAt, updatedAt (Date) — managed automatically; updatedAt is refreshed via a pre-update hook All changes are captured in the Audits collection via a MongoDB change-stream watch. CORE FIELDS label (string, required) — display name caption (string, default "") — short description isActive (boolean, default false) — when false the UI marks the connector as disabled; runtime still executes it if referenced by a block method (string, required, enum): "get" | "post" | "put" | "patch" | "delete" | "options" url (string, required) — absolute target URL. Supports the template DSL (see TEMPLATE DSL below). headers (string) — JSON-ENCODED STRING (not an object). Ace editor on the UI edits it as JSON; Engine does JSON.parse(headers) at call time after template substitution. Include Content-Type, Authorization, etc. data (string) — JSON-ENCODED STRING request body. Same parsing semantics as headers. Used for POST/PUT/PATCH. auth (object, OPTIONAL, SECRETS) — HTTP Basic Auth. Schema supports { auth.username, auth.password } but note the UI does NOT currently expose an auth form; most callers inline auth into the headers JSON instead. DEAD / UNUSED FIELDS (present in schema but not touched by the current engine — don't rely on them) translate (Array) — intended as [{ label, value }] response-field mapping but NOT applied anywhere in engine/Engine.js. Agents can leave it empty. params (string) — present on schema but the UI doesn't edit it and Engine.js does not substitute or forward it; inline query params into url instead. warning (boolean) — UI-only flag, never read/written by the engine. TEMPLATE DSL (applied to url, headers, data at invocation time) Placeholder syntax: $.<namespace>.<path> — FlowHelper.parseDataAccess() scans and resolves each occurrence. Path supports dot traversal and array indexing ([0], [-1]). Available namespaces in refs: $.contact.<field> — contact / CRM Person fields (e.g. $.contact.phone) $.interaction.<field> — interaction metadata (e.g. $.interaction._id) $.crm.<fieldId> — CRM custom field values by field id $.addons.<addonId> — addon outputs (CSAT score, GPT answer, etc.) by addon id $.connectors.<connectorId> — PRIOR connector responses by connector id (chain connectors together) $.evidences.<addonId> — evidence messages captured by an addon $.blocks.<blockId> — block state (rarely used directly) Example: url = "https://api.vendor.io/customers/$.contact.externalId/orders?limit=5" RUNTIME BEHAVIOR 1. Engine.js loads the connector doc on demand (inline by connectorId reference on a Flow block's context, or via the $fetchConnector() flow expression). See engine/Engine.js:~4261 and ~4602. 2. Template-substitutes url / headers / data against the current refs. 3. JSON.parses headers (default {} if empty) and data (default null if empty). 4. Issues axios({ method, url, headers, data }). Axios throws on non-2xx. 5. On success: refs.connectors[<connectorId>] = response.data ?? 'No Content' + persists to interaction. 6. On error: extracts e.response.data (else e.request / e.message) into the SAME slot. There is NO status-code branching — success and error bodies occupy the same refs.connectors[<connectorId>] value; downstream blocks can't easily tell them apart. Validate post-response with an addon of type "condition" if you need error routing. 7. The Gupshup webhook (routes/webhooks/gupshup.js) writes inline integration results (e.g. Eloqua) directly into refs.connectors[<key>] WITHOUT going through the connector document — those entries live in the same namespace and can be referenced via $.connectors.<key>. RELATIONSHIPS Group 1 ── N Connector.groupId Account 1 ── N Connector.accountId Connector referenced by Flows — Flow block contexts point at a connector via contexts[].connectorId Connector referenced by Addons indirectly — some addon types bind to a connector via their options Connector referenced by Agents — agent tool-calling can invoke connectors as external lookups SECRETS NOTE Anything inlined into headers (API keys, bearer tokens) and auth.* is effectively secret. MCP callers with update access can read current values; handle payloads accordingly and prefer rotating credentials via a dedicated secret store + referencing via environment injection upstream when possible. ACTIONS get { id } — fetch one connector (auto-scoped) list { query, project?, options? } — returns { count, items[] }. Default options.limit=10, sorted by createdAt desc. Common shapes: { isActive: true } — enabled connectors { method: "post" } — all POST connectors { label: { $regex: "crm", $options: "i" } } — partial name search { url: { $regex: "^https://api\\." } } — by target domain create { payload } — required: label, method, url. Typically also: headers (JSON string), data (JSON string), auth{username,password} if using basic auth. groupId/accountId server-injected. translate/params/warning can be omitted (dead fields). update { id, payload, project? } — any mutable field. To disable without deleting, set isActive:false. To rotate credentials, update auth.* or the Authorization header in headers. delete { id } — soft-delete (sets status:false). Prior runtime outputs already persisted on interactions are NOT purged. EXAMPLES list { query: { method: "post", isActive: true }, options: { limit: 20 } } create { payload: { label: "Get customer orders", method: "get", url: "https://api.vendor.io/customers/$.contact.externalId/orders?limit=5", headers: "{\"Authorization\": \"Bearer <token>\", \"Content-Type\": \"application/json\"}", data: "", isActive: true } } create { payload: { label: "Create ticket", method: "post", url: "https://support.example.com/api/tickets", headers: "{\"X-Api-Key\": \"<key>\", \"Content-Type\": \"application/json\"}", data: "{\"subject\": \"$.addons.<intentAddonId>\", \"contactId\": \"$.contact.externalId\", \"notes\": \"$.evidences.<notesAddonId>\"}", isActive: true } } update { id: "<connectorId>", payload: { isActive: false } }

smarttalks_connectors_manage

ChatGPT
Connectors — REUSABLE OUTBOUND HTTP CALL DEFINITIONS. A connector is a named, tenant-scoped HTTP request template (method + url + headers + data) with per-flow template substitution, used by Flows/Addons/Agents to integrate with external systems. At runtime, Engine resolves template placeholders against the active flow context, issues the request via axios, and PERSISTS the raw response body under refs.connectors[<connectorId>] — both in memory for downstream template lookups and on the interaction document (field connectors.<connectorId>) — so later flow steps can reference the result via the DSL. Tenancy is enforced server-side. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters to status:true createdAt, updatedAt (Date) — managed automatically; updatedAt is refreshed via a pre-update hook All changes are captured in the Audits collection via a MongoDB change-stream watch. CORE FIELDS label (string, required) — display name caption (string, default "") — short description isActive (boolean, default false) — when false the UI marks the connector as disabled; runtime still executes it if referenced by a block method (string, required, enum): "get" | "post" | "put" | "patch" | "delete" | "options" url (string, required) — absolute target URL. Supports the template DSL (see TEMPLATE DSL below). headers (string) — JSON-ENCODED STRING (not an object). Ace editor on the UI edits it as JSON; Engine does JSON.parse(headers) at call time after template substitution. Include Content-Type, Authorization, etc. data (string) — JSON-ENCODED STRING request body. Same parsing semantics as headers. Used for POST/PUT/PATCH. auth (object, OPTIONAL, SECRETS) — HTTP Basic Auth. Schema supports { auth.username, auth.password } but note the UI does NOT currently expose an auth form; most callers inline auth into the headers JSON instead. DEAD / UNUSED FIELDS (present in schema but not touched by the current engine — don't rely on them) translate (Array) — intended as [{ label, value }] response-field mapping but NOT applied anywhere in engine/Engine.js. Agents can leave it empty. params (string) — present on schema but the UI doesn't edit it and Engine.js does not substitute or forward it; inline query params into url instead. warning (boolean) — UI-only flag, never read/written by the engine. TEMPLATE DSL (applied to url, headers, data at invocation time) Placeholder syntax: $.<namespace>.<path> — FlowHelper.parseDataAccess() scans and resolves each occurrence. Path supports dot traversal and array indexing ([0], [-1]). Available namespaces in refs: $.contact.<field> — contact / CRM Person fields (e.g. $.contact.phone) $.interaction.<field> — interaction metadata (e.g. $.interaction._id) $.crm.<fieldId> — CRM custom field values by field id $.addons.<addonId> — addon outputs (CSAT score, GPT answer, etc.) by addon id $.connectors.<connectorId> — PRIOR connector responses by connector id (chain connectors together) $.evidences.<addonId> — evidence messages captured by an addon $.blocks.<blockId> — block state (rarely used directly) Example: url = "https://api.vendor.io/customers/$.contact.externalId/orders?limit=5" RUNTIME BEHAVIOR 1. Engine.js loads the connector doc on demand (inline by connectorId reference on a Flow block's context, or via the $fetchConnector() flow expression). See engine/Engine.js:~4261 and ~4602. 2. Template-substitutes url / headers / data against the current refs. 3. JSON.parses headers (default {} if empty) and data (default null if empty). 4. Issues axios({ method, url, headers, data }). Axios throws on non-2xx. 5. On success: refs.connectors[<connectorId>] = response.data ?? 'No Content' + persists to interaction. 6. On error: extracts e.response.data (else e.request / e.message) into the SAME slot. There is NO status-code branching — success and error bodies occupy the same refs.connectors[<connectorId>] value; downstream blocks can't easily tell them apart. Validate post-response with an addon of type "condition" if you need error routing. 7. The Gupshup webhook (routes/webhooks/gupshup.js) writes inline integration results (e.g. Eloqua) directly into refs.connectors[<key>] WITHOUT going through the connector document — those entries live in the same namespace and can be referenced via $.connectors.<key>. RELATIONSHIPS Group 1 ── N Connector.groupId Account 1 ── N Connector.accountId Connector referenced by Flows — Flow block contexts point at a connector via contexts[].connectorId Connector referenced by Addons indirectly — some addon types bind to a connector via their options Connector referenced by Agents — agent tool-calling can invoke connectors as external lookups SECRETS NOTE Anything inlined into headers (API keys, bearer tokens) and auth.* is effectively secret. MCP callers with update access can read current values; handle payloads accordingly and prefer rotating credentials via a dedicated secret store + referencing via environment injection upstream when possible. ACTIONS get { id } — fetch one connector (auto-scoped) list { query, project?, options? } — returns { count, items[] }. Default options.limit=10, sorted by createdAt desc. Common shapes: { isActive: true } — enabled connectors { method: "post" } — all POST connectors { label: { $regex: "crm", $options: "i" } } — partial name search { url: { $regex: "^https://api\\." } } — by target domain create { payload } — required: label, method, url. Typically also: headers (JSON string), data (JSON string), auth{username,password} if using basic auth. groupId/accountId server-injected. translate/params/warning can be omitted (dead fields). update { id, payload, project? } — any mutable field. To disable without deleting, set isActive:false. To rotate credentials, update auth.* or the Authorization header in headers. delete { id } — soft-delete (sets status:false). Prior runtime outputs already persisted on interactions are NOT purged. EXAMPLES list { query: { method: "post", isActive: true }, options: { limit: 20 } } create { payload: { label: "Get customer orders", method: "get", url: "https://api.vendor.io/customers/$.contact.externalId/orders?limit=5", headers: "{\"Authorization\": \"Bearer <token>\", \"Content-Type\": \"application/json\"}", data: "", isActive: true } } create { payload: { label: "Create ticket", method: "post", url: "https://support.example.com/api/tickets", headers: "{\"X-Api-Key\": \"<key>\", \"Content-Type\": \"application/json\"}", data: "{\"subject\": \"$.addons.<intentAddonId>\", \"contactId\": \"$.contact.externalId\", \"notes\": \"$.evidences.<notesAddonId>\"}", isActive: true } } update { id: "<connectorId>", payload: { isActive: false } }

smarttalks_content

ChatGPT
Content — KNOWLEDGE MODELS for retrieval-augmented (RAG) question answering. A "content" record is the configuration of one knowledge base: its name, prompts, embedding model, LLM provider, and how many similar documents to retrieve per query. The actual knowledge (text chunks/embeddings) lives in the related TrainContent collection, which this tool does NOT manage. Every record is auto-scoped to the caller's tenant (groupId + accountId injected server-side from the JWT). INPUT SHAPE { action, ...args } IDENTITY & TENANCY (server-managed — never send these) _id (ObjectId) groupId, accountId (ObjectId) — tenant scope, injected from the JWT status (boolean, default true) — soft-delete flag; list excludes status:false by default createdAt, updatedAt (Date), updatedBy (string) contents (number), size (string), trainingTime (number[]), totalTokens (number[]) — computed counters CLIENT-SETTABLE FIELDS (update payload) name (string) — display name of the knowledge model isActive (boolean, default false) — whether the model is published/active gptResponse (boolean, default true) — generate an LLM answer during predict (false = retrieval only) contentType (string) — free-form label similarities (number, default 3) — how many top documents RAG retrieves per query modelType (string, default "text-embedding-3-small") — embedding model validationPrompt (string) — system prompt for answer validation responsePrompt (string) — system prompt for answer generation modelProvider (object, default { label: "GPT-4.1", value: "gpt-4.1" }) — { label, value }; value must start with "gpt-" or "gemini-" ACTIONS list { query?, project?, options? } POST /v1/content — paginated list for the caller's account. query (Mongo filter), project (Mongo projection), options ({ skip?, limit? (default 100), sort? }). Response: { count, items[] }. get { id } GET /v1/content/:id — one knowledge model by _id. update { id, payload, project? } PATCH /v1/content/:id — $set partial update. Server-managed fields are rejected. delete { id } DELETE /v1/content/:id — soft-delete (status:false). CASCADES: linked TrainContent is soft-deleted; ContentAnalytics and ContentReview records are HARD-deleted (unrecoverable). changeStatus { id } PATCH /v1/content/:id/changeStatus — restore a soft-deleted model (status back to true); also restores its linked TrainContent. trashCan { } POST /v1/content/trashCan — count of soft-deleted models: { count }. readTrash { } POST /v1/content/readTrash — list soft-deleted models: { count, items[] }. predict { id, payload } PUT /v1/content/:id/predict — run a RAG query against the model. payload: { question (string, required), explainability? (boolean — return top keywords), environment? ("test" | "production"), channel?, interactionId?, blockId?, flowId?, blockLabel?, flowLabel? }. With environment other than "test", the server writes a ContentAnalytics record. Response: { question, context: [{ doc, similarity }], answer, validation, validationExplanation, keyWords[], executionTimeInSeconds, tokenUsage, ...timings }. trainContent { id, payload } PATCH /v1/content/:id/trainContent — (re)train embeddings for the model. payload is forwarded verbatim; pass {} to retrain with the model's current settings. metrics { query? } — POST /v1/content/metrics/beta — aggregated call counts. modelMetrics { query? } — POST /v1/content/metrics/model/beta — per-model accuracy + latency. validation { query? } — POST /v1/content/validation/beta — validation accuracy stats. table { query? } — POST /v1/content/table/beta — paginated detailed analytics rows. blockFlow { query? } — POST /v1/content/blockFlow/beta — flows/blocks that used the models. Analytics query is a Mongo-ish filter, commonly: { createdAt: { $gt, $lt }, $and: [{ modelId: { $in: [...] } }] }. EXAMPLES list { options: { limit: 20 } } get { id: "<contentId>" } update { id: "<contentId>", payload: { similarities: 5, gptResponse: true } } predict { id: "<contentId>", payload: { question: "What is the refund policy?", environment: "test" } } trainContent { id: "<contentId>", payload: {} } changeStatus { id: "<contentId>" } modelMetrics { query: { createdAt: { $gt: "2026-05-01", $lt: "2026-05-31" } } } NOT EXPOSED create — no usable endpoint. The authenticated PUT /v1/content route is wired upstream to read, not create. The only create handler is origin-locked to the App frontend and unreachable from this server. processFile — multipart binary file upload; not expressible as an MCP tool call. webScrape — long-running ingestion behind multipart middleware. violin / wordCloud / generalGraph analytics — raw chart-rendering data with no agent value. PATCH /contentAnalytics/:id — low-intent internal analytics-record patching.

smarttalks_content_manage

ChatGPT
Content — KNOWLEDGE MODELS for retrieval-augmented (RAG) question answering. A "content" record is the configuration of one knowledge base: its name, prompts, embedding model, LLM provider, and how many similar documents to retrieve per query. The actual knowledge (text chunks/embeddings) lives in the related TrainContent collection, which this tool does NOT manage. Every record is auto-scoped to the caller's tenant (groupId + accountId injected server-side from the JWT). INPUT SHAPE { action, ...args } IDENTITY & TENANCY (server-managed — never send these) _id (ObjectId) groupId, accountId (ObjectId) — tenant scope, injected from the JWT status (boolean, default true) — soft-delete flag; list excludes status:false by default createdAt, updatedAt (Date), updatedBy (string) contents (number), size (string), trainingTime (number[]), totalTokens (number[]) — computed counters CLIENT-SETTABLE FIELDS (update payload) name (string) — display name of the knowledge model isActive (boolean, default false) — whether the model is published/active gptResponse (boolean, default true) — generate an LLM answer during predict (false = retrieval only) contentType (string) — free-form label similarities (number, default 3) — how many top documents RAG retrieves per query modelType (string, default "text-embedding-3-small") — embedding model validationPrompt (string) — system prompt for answer validation responsePrompt (string) — system prompt for answer generation modelProvider (object, default { label: "GPT-4.1", value: "gpt-4.1" }) — { label, value }; value must start with "gpt-" or "gemini-" ACTIONS list { query?, project?, options? } POST /v1/content — paginated list for the caller's account. query (Mongo filter), project (Mongo projection), options ({ skip?, limit? (default 100), sort? }). Response: { count, items[] }. get { id } GET /v1/content/:id — one knowledge model by _id. update { id, payload, project? } PATCH /v1/content/:id — $set partial update. Server-managed fields are rejected. delete { id } DELETE /v1/content/:id — soft-delete (status:false). CASCADES: linked TrainContent is soft-deleted; ContentAnalytics and ContentReview records are HARD-deleted (unrecoverable). changeStatus { id } PATCH /v1/content/:id/changeStatus — restore a soft-deleted model (status back to true); also restores its linked TrainContent. trashCan { } POST /v1/content/trashCan — count of soft-deleted models: { count }. readTrash { } POST /v1/content/readTrash — list soft-deleted models: { count, items[] }. predict { id, payload } PUT /v1/content/:id/predict — run a RAG query against the model. payload: { question (string, required), explainability? (boolean — return top keywords), environment? ("test" | "production"), channel?, interactionId?, blockId?, flowId?, blockLabel?, flowLabel? }. With environment other than "test", the server writes a ContentAnalytics record. Response: { question, context: [{ doc, similarity }], answer, validation, validationExplanation, keyWords[], executionTimeInSeconds, tokenUsage, ...timings }. trainContent { id, payload } PATCH /v1/content/:id/trainContent — (re)train embeddings for the model. payload is forwarded verbatim; pass {} to retrain with the model's current settings. metrics { query? } — POST /v1/content/metrics/beta — aggregated call counts. modelMetrics { query? } — POST /v1/content/metrics/model/beta — per-model accuracy + latency. validation { query? } — POST /v1/content/validation/beta — validation accuracy stats. table { query? } — POST /v1/content/table/beta — paginated detailed analytics rows. blockFlow { query? } — POST /v1/content/blockFlow/beta — flows/blocks that used the models. Analytics query is a Mongo-ish filter, commonly: { createdAt: { $gt, $lt }, $and: [{ modelId: { $in: [...] } }] }. EXAMPLES list { options: { limit: 20 } } get { id: "<contentId>" } update { id: "<contentId>", payload: { similarities: 5, gptResponse: true } } predict { id: "<contentId>", payload: { question: "What is the refund policy?", environment: "test" } } trainContent { id: "<contentId>", payload: {} } changeStatus { id: "<contentId>" } modelMetrics { query: { createdAt: { $gt: "2026-05-01", $lt: "2026-05-31" } } } NOT EXPOSED create — no usable endpoint. The authenticated PUT /v1/content route is wired upstream to read, not create. The only create handler is origin-locked to the App frontend and unreachable from this server. processFile — multipart binary file upload; not expressible as an MCP tool call. webScrape — long-running ingestion behind multipart middleware. violin / wordCloud / generalGraph analytics — raw chart-rendering data with no agent value. PATCH /contentAnalytics/:id — low-intent internal analytics-record patching.

smarttalks_contracts

ChatGPT
Contracts — Billing contracts linked to Clients, with setup/monthly values, variable pricing models, and Conta Azul integration. ACTIONS list { clientId?, status?, type?, page?, limit? } — List all contracts with optional filters get { id } — Fetch one contract by ID (includes populated clientIds, accountIds, unitIds, and charges virtual) create { payload } — Create new contract. Required: clientIds[], number, type, status Each clientId, accountId, unitId in payload must exist update { id, payload } — Update contract fields. _id is protected delete { id } — Soft-delete contract. BLOCKS if active charges exist with status ativa/pausada/encerrada

smarttalks_contracts_manage

ChatGPT
Contracts — Billing contracts linked to Clients, with setup/monthly values, variable pricing models, and Conta Azul integration. ACTIONS list { clientId?, status?, type?, page?, limit? } — List all contracts with optional filters get { id } — Fetch one contract by ID (includes populated clientIds, accountIds, unitIds, and charges virtual) create { payload } — Create new contract. Required: clientIds[], number, type, status Each clientId, accountId, unitId in payload must exist update { id, payload } — Update contract fields. _id is protected delete { id } — Soft-delete contract. BLOCKS if active charges exist with status ativa/pausada/encerrada

smarttalks_departments

ChatGPT
Departments — LOGICAL GROUPINGS OF USERS within an Account. A department collects users under a named organisational unit and defines the idle-state policy that governs their automatic presence transitions. IDENTITY & TENANCY _id (ObjectId) — primary key groupId (ObjectId, required, server-injected) — parent tenant Group; taken from caller's JWT, never supplied by the client accountId (ObjectId, required, server-injected) — parent Account; same injection rule as groupId All read/write operations are automatically scoped to the caller's groupId + accountId pair. PROFILE label (string, required) — human-readable display name shown in UIs and reports caption (string, default "") — optional short description or sub-label for the department TIMEOUT POLICY — USER IDLE LIFECYCLE STATE MACHINE Departments carry three cascading timeout values that drive automatic presence transitions for all users belonging to the department. The state machine is: Online → (awayTimeout) → Away → (offlineTimeout) → Offline → (logoffTimeout) → LoggedOff awayTimeout (number, seconds, default 0) Triggered when: a user is Online and has been idle (no interaction activity) for this many seconds. Transition: Online → Away. 0 = disabled — the user remains Online until they manually change state or disconnect. offlineTimeout (number, seconds, default 0) Triggered when: a user has been in the Away state for this many seconds without resuming activity. Transition: Away → Offline. 0 = disabled — the user stays Away indefinitely until manual action or a downstream timeout fires. logoffTimeout (number, seconds, default 0) Triggered when: a user has been Offline (but still technically logged in) for this many seconds. Transition: Offline → LoggedOff (session terminated). 0 = disabled — the session persists in Offline state; useful for departments that should not auto-expire sessions. The three timeouts are independent; setting any subset is valid. Setting all three to 0 disables the entire idle-lifecycle enforcement for the department. LIFECYCLE status (boolean, default true) — soft-delete flag. The Mongoose pre-find and pre-count middleware automatically filter to status:true, so inactive departments are invisible to normal queries. Use delete action to set status:false. createdAt, updatedAt (Date) — managed automatically; updatedAt is refreshed via a pre-update hook. Every change to a department document is captured in the Audits collection via a MongoDB change-stream watch. RELATIONSHIPS Group 1 ── N Department.groupId Account 1 ── N Department.accountId Department 1 ── N User.department — every user belongs to exactly one department; the foreign key lives on the user side (field: department on smarttalks_users) Queue N ── M Department via Queue.departments[] — a queue staffs one or more departments; the foreign key lives on the queue side (field: departments[] on smarttalks_queues) ACTIONS get { id } — fetch one department (auto-scoped to caller's group/account) list { query, project?, options? } — returns { count, items[] }. Default options.limit = 10. Common query shapes: { label: { $regex: "support", $options: "i" } } — partial name search { awayTimeout: { $gt: 0 } } — departments with active away policy { offlineTimeout: 0, logoffTimeout: 0 } — departments with no auto-logoff {} — all active departments in account create { payload } — required: label. Optional: caption, awayTimeout, offlineTimeout, logoffTimeout. groupId/accountId are server-injected from the caller's JWT. update { id, payload } — mutate label, caption, or any timeout field delete { id } — soft delete (sets status:false); hidden from subsequent list/find queries

smarttalks_departments_manage

ChatGPT
Departments — LOGICAL GROUPINGS OF USERS within an Account. A department collects users under a named organisational unit and defines the idle-state policy that governs their automatic presence transitions. IDENTITY & TENANCY _id (ObjectId) — primary key groupId (ObjectId, required, server-injected) — parent tenant Group; taken from caller's JWT, never supplied by the client accountId (ObjectId, required, server-injected) — parent Account; same injection rule as groupId All read/write operations are automatically scoped to the caller's groupId + accountId pair. PROFILE label (string, required) — human-readable display name shown in UIs and reports caption (string, default "") — optional short description or sub-label for the department TIMEOUT POLICY — USER IDLE LIFECYCLE STATE MACHINE Departments carry three cascading timeout values that drive automatic presence transitions for all users belonging to the department. The state machine is: Online → (awayTimeout) → Away → (offlineTimeout) → Offline → (logoffTimeout) → LoggedOff awayTimeout (number, seconds, default 0) Triggered when: a user is Online and has been idle (no interaction activity) for this many seconds. Transition: Online → Away. 0 = disabled — the user remains Online until they manually change state or disconnect. offlineTimeout (number, seconds, default 0) Triggered when: a user has been in the Away state for this many seconds without resuming activity. Transition: Away → Offline. 0 = disabled — the user stays Away indefinitely until manual action or a downstream timeout fires. logoffTimeout (number, seconds, default 0) Triggered when: a user has been Offline (but still technically logged in) for this many seconds. Transition: Offline → LoggedOff (session terminated). 0 = disabled — the session persists in Offline state; useful for departments that should not auto-expire sessions. The three timeouts are independent; setting any subset is valid. Setting all three to 0 disables the entire idle-lifecycle enforcement for the department. LIFECYCLE status (boolean, default true) — soft-delete flag. The Mongoose pre-find and pre-count middleware automatically filter to status:true, so inactive departments are invisible to normal queries. Use delete action to set status:false. createdAt, updatedAt (Date) — managed automatically; updatedAt is refreshed via a pre-update hook. Every change to a department document is captured in the Audits collection via a MongoDB change-stream watch. RELATIONSHIPS Group 1 ── N Department.groupId Account 1 ── N Department.accountId Department 1 ── N User.department — every user belongs to exactly one department; the foreign key lives on the user side (field: department on smarttalks_users) Queue N ── M Department via Queue.departments[] — a queue staffs one or more departments; the foreign key lives on the queue side (field: departments[] on smarttalks_queues) ACTIONS get { id } — fetch one department (auto-scoped to caller's group/account) list { query, project?, options? } — returns { count, items[] }. Default options.limit = 10. Common query shapes: { label: { $regex: "support", $options: "i" } } — partial name search { awayTimeout: { $gt: 0 } } — departments with active away policy { offlineTimeout: 0, logoffTimeout: 0 } — departments with no auto-logoff {} — all active departments in account create { payload } — required: label. Optional: caption, awayTimeout, offlineTimeout, logoffTimeout. groupId/accountId are server-injected from the caller's JWT. update { id, payload } — mutate label, caption, or any timeout field delete { id } — soft delete (sets status:false); hidden from subsequent list/find queries

smarttalks_entities

ChatGPT
Entities — SCHEMA DEFINITIONS for CRM object types. An entity is the TEMPLATE (structure + field layout) for a class of CRM records (Items). Every entity is scoped to one Group and one Account. Built-in system types are seeded on first-time account setup; custom types can be created freely. FIELDS _id (ObjectId), status (boolean, default true) — soft-delete; list queries hide status:false by default groupId (ObjectId, required, server-injected on create) — parent tenant Group accountId (ObjectId, required, server-injected on create) — parent Account units[] (ObjectId[]) — restricts visibility to users belonging to these units; empty = visible to all type (number, default 999) — entity type numeric code; see BUILT-IN TYPES below title (string, default "Contatos") — display name of this entity description (string) — short description shown in the UI folder (string, default "") — optional grouping folder label isSystem (boolean, default false) — marks system-seeded entities; do NOT set to true via MCP showUnits (boolean, default false) — show units column in the items list showClient (boolean, default false) — show client column in the items list showResponsible (boolean, default false) — show responsible column in the items list showTags (boolean, default false) — show tags column in the items list settings (object, default {}) — free-form per-entity settings bag; also indexed as settings.status permissions (object) — access control: permissions.read[] (ObjectId[]) — permissionsGroups that can read; empty = unrestricted permissions.write[] (ObjectId[]) — permissionsGroups that can write; empty = unrestricted createdAt, updatedAt (Date) LAYOUT (array of sections) Each section in layout[] defines one panel in the entity form: layout[].icon (string) — icon name for the section layout[].label (string) — section display name layout[].description (string) layout[].flex (string, default "col-12") — CSS flex/grid class layout[].height (string, default "100vh") layout[].isSystem (boolean, default false) layout[].type (number) — section type: 0 = DEFAULT, 1 = TIMELINE layout[].permissions.read[], layout[].permissions.write[] — section-level ACL layout[].fields[] — the fields inside this section (see FIELD SCHEMA below) FIELD SCHEMA (layout[].fields[]) Each field object: isSystem (boolean, default false) flex (string) — CSS class, e.g. "col-12", "col-6", "col-4" label (string, required) — field display name description (string), placeholder (string), value (string) — hints / defaults isRequired (boolean, default false) isEditable (boolean, default true) isPrimary (boolean, default false) — marks the field as an entity primary key field (e.g. WhatsApp ID, Instagram ID) type (number, required) — field type code; see FIELD TYPES below subtype (string) — optional sub-type discriminator options (array) — static options for SELECT / SELECT_MULTIPLE / SCHEDULE_STATUS fields option (object) — single-option config mask (string, default "") — input mask pattern foreignEntityId (ObjectId, default null) — for OBJECT_ID / OBJECT_ID_MULTIPLE fields, references another entity permissions.exhibition[] — display context codes (1=OPTIONS, 2=COLUMN, 3=COLUMN_FIXED, 4=LABEL, 5=TABLE, 30=WIDGET, 31=WHATSAPP, 32=MESSENGER, 33=INSTAGRAM, 40=LANDING_PAGE, 41=LANDING_PAGE_HIDE) permissions.read[], permissions.write[] — field-level ACL FIELD TYPES (type numeric codes) 1=STRING, 2=NUMBER, 3=DATE, 4=CONTACT_ID, 5=BOOLEAN 6=OBJECT_ID_MULTIPLE (internal multi-ref), 7=OBJECT_ID (internal single-ref) 8=ARRAY, 9=MASK, 10=EMAIL, 11=PHONE, 12=AVATAR, 13=NAME 14=CNPJ, 15=CPF, 16=SEX, 17=CEP, 18=MAP 19=SELECT, 20=SELECT_MULTIPLE 21=ADDRESS, 22=ADDRESS_DISTRICT, 23=ADDRESS_NUMBER, 24=ADDRESS_COMP, 25=ADDRESS_UF, 26=ADDRESS_CITY 27=CURRENCY, 28=MASS_UNIT, 29=DISCOVER, 30=TEXT, 31=COUNTRY, 32=TABLE, 33=URL 34=WHATSAPP_ID, 35=INSTAGRAM_ID, 36=MESSENGER_ID 37=OPT_IN, 38=TAGS, 39=LANGUAGE, 40=LANGUAGE_MULTIPLE 41=SCHEDULE_DATE, 42=SCHEDULE_TIME, 43=SCHEDULE_GRAPH, 44=SCHEDULE_QUANTITY, 45=SCHEDULE_MULTIPLIER, 46=SCHEDULE_STATUS 50=FILTER, 51=INFO, 52=ARCHIVES, 53=UNIQUE_ID, 54=RATING, 55=STATUS, 56=USER_ID, 57=MERGE BUILT-IN ENTITY TYPES (type field) 1 = CONTACT — contact records (people); seeded on first-time setup with Avatar, Name, Email, WhatsApp ID, Instagram ID, Messenger ID, Opt-in, Tags fields 2 = CONTENT — content / company records; built from a shared template 3 = SALES — sales pipeline records; built from a shared template 4 = SERVICES — service records; built from a shared template 5 = SCHEDULE — scheduling records; auto-layout with SCHEDULE_DATE, SCHEDULE_TIME, SCHEDULE_QUANTITY, SCHEDULE_STATUS fields; SCHEDULE_STATUS options: scheduled, rescheduled, canceled, show, noshow, done 10 = NPS — NPS/survey records; auto-layout with RATING, TEXT (feedback), STRING (category) fields 999 = CUSTOM — user-defined custom entity (default for create) Types 2, 3, 4 can be bootstrapped from server-side templates by passing template: <type> in the create payload. DELETE GUARD delete is BLOCKED (returns an error) if the entity has any active Items (Items.status:true) associated with it. You must delete or archive all items first. PERMISSIONS ENFORCEMENT delete checks permissions.write — the caller's permissionsGroups must intersect permissions.write, or permissions.write must be empty. list automatically filters by auth.units (when the caller is unit-scoped) and by auth.entities (entity-level access list from the token). RELATIONSHIPS Group 1 ── N Entity.groupId Account 1 ── N Entity.accountId Entity 1 ── N Items — the records (rows) that conform to this entity's schema; see smarttalks_items Entity.layout[].fields[].foreignEntityId → Entity — cross-entity reference for OBJECT_ID / OBJECT_ID_MULTIPLE fields Entity N ── M PermissionsGroups via permissions.read[] / permissions.write[] (smarttalks_permissions_groups) Entity N ── M Units via units[] (smarttalks_units) ACTIONS get { id } — fetch one entity (full document including layout) list { query, project?, options? } — Mongo filter; server always injects groupId + accountId + status:true. Common filters: { type: 1 } — all CONTACT entities { type: 999 } — all custom entities { isSystem: true } — system-seeded entities only { folder: "<name>" } — entities in a folder { "permissions.read": "<pgId>" } — entities readable by a permissionsGroup Default project (when none given): units, createdAt, icon, title, description, showUnits, showClient, showResponsible, showTags, permissions, layout options supports { limit, skip, sort } create { payload } — required: title. Optional: type (default 999), description, folder, layout[], permissions, units[], settings. Shortcut: include template: <2|3|4|5|10> to bootstrap from a server-side template (sets layout automatically). groupId/accountId are server-injected — do NOT include. Do NOT set isSystem: true. update { id, payload } — mutate any mutable field. groupId and accountId are IMMUTABLE — do NOT include. To replace the full layout[], send the complete layout array (merge semantics: $set on the whole field). _id and __v are stripped server-side before the update. delete { id } — soft-delete (sets status:false). BLOCKED if any active Items exist for this entity.

smarttalks_entities_manage

ChatGPT
Entities — SCHEMA DEFINITIONS for CRM object types. An entity is the TEMPLATE (structure + field layout) for a class of CRM records (Items). Every entity is scoped to one Group and one Account. Built-in system types are seeded on first-time account setup; custom types can be created freely. FIELDS _id (ObjectId), status (boolean, default true) — soft-delete; list queries hide status:false by default groupId (ObjectId, required, server-injected on create) — parent tenant Group accountId (ObjectId, required, server-injected on create) — parent Account units[] (ObjectId[]) — restricts visibility to users belonging to these units; empty = visible to all type (number, default 999) — entity type numeric code; see BUILT-IN TYPES below title (string, default "Contatos") — display name of this entity description (string) — short description shown in the UI folder (string, default "") — optional grouping folder label isSystem (boolean, default false) — marks system-seeded entities; do NOT set to true via MCP showUnits (boolean, default false) — show units column in the items list showClient (boolean, default false) — show client column in the items list showResponsible (boolean, default false) — show responsible column in the items list showTags (boolean, default false) — show tags column in the items list settings (object, default {}) — free-form per-entity settings bag; also indexed as settings.status permissions (object) — access control: permissions.read[] (ObjectId[]) — permissionsGroups that can read; empty = unrestricted permissions.write[] (ObjectId[]) — permissionsGroups that can write; empty = unrestricted createdAt, updatedAt (Date) LAYOUT (array of sections) Each section in layout[] defines one panel in the entity form: layout[].icon (string) — icon name for the section layout[].label (string) — section display name layout[].description (string) layout[].flex (string, default "col-12") — CSS flex/grid class layout[].height (string, default "100vh") layout[].isSystem (boolean, default false) layout[].type (number) — section type: 0 = DEFAULT, 1 = TIMELINE layout[].permissions.read[], layout[].permissions.write[] — section-level ACL layout[].fields[] — the fields inside this section (see FIELD SCHEMA below) FIELD SCHEMA (layout[].fields[]) Each field object: isSystem (boolean, default false) flex (string) — CSS class, e.g. "col-12", "col-6", "col-4" label (string, required) — field display name description (string), placeholder (string), value (string) — hints / defaults isRequired (boolean, default false) isEditable (boolean, default true) isPrimary (boolean, default false) — marks the field as an entity primary key field (e.g. WhatsApp ID, Instagram ID) type (number, required) — field type code; see FIELD TYPES below subtype (string) — optional sub-type discriminator options (array) — static options for SELECT / SELECT_MULTIPLE / SCHEDULE_STATUS fields option (object) — single-option config mask (string, default "") — input mask pattern foreignEntityId (ObjectId, default null) — for OBJECT_ID / OBJECT_ID_MULTIPLE fields, references another entity permissions.exhibition[] — display context codes (1=OPTIONS, 2=COLUMN, 3=COLUMN_FIXED, 4=LABEL, 5=TABLE, 30=WIDGET, 31=WHATSAPP, 32=MESSENGER, 33=INSTAGRAM, 40=LANDING_PAGE, 41=LANDING_PAGE_HIDE) permissions.read[], permissions.write[] — field-level ACL FIELD TYPES (type numeric codes) 1=STRING, 2=NUMBER, 3=DATE, 4=CONTACT_ID, 5=BOOLEAN 6=OBJECT_ID_MULTIPLE (internal multi-ref), 7=OBJECT_ID (internal single-ref) 8=ARRAY, 9=MASK, 10=EMAIL, 11=PHONE, 12=AVATAR, 13=NAME 14=CNPJ, 15=CPF, 16=SEX, 17=CEP, 18=MAP 19=SELECT, 20=SELECT_MULTIPLE 21=ADDRESS, 22=ADDRESS_DISTRICT, 23=ADDRESS_NUMBER, 24=ADDRESS_COMP, 25=ADDRESS_UF, 26=ADDRESS_CITY 27=CURRENCY, 28=MASS_UNIT, 29=DISCOVER, 30=TEXT, 31=COUNTRY, 32=TABLE, 33=URL 34=WHATSAPP_ID, 35=INSTAGRAM_ID, 36=MESSENGER_ID 37=OPT_IN, 38=TAGS, 39=LANGUAGE, 40=LANGUAGE_MULTIPLE 41=SCHEDULE_DATE, 42=SCHEDULE_TIME, 43=SCHEDULE_GRAPH, 44=SCHEDULE_QUANTITY, 45=SCHEDULE_MULTIPLIER, 46=SCHEDULE_STATUS 50=FILTER, 51=INFO, 52=ARCHIVES, 53=UNIQUE_ID, 54=RATING, 55=STATUS, 56=USER_ID, 57=MERGE BUILT-IN ENTITY TYPES (type field) 1 = CONTACT — contact records (people); seeded on first-time setup with Avatar, Name, Email, WhatsApp ID, Instagram ID, Messenger ID, Opt-in, Tags fields 2 = CONTENT — content / company records; built from a shared template 3 = SALES — sales pipeline records; built from a shared template 4 = SERVICES — service records; built from a shared template 5 = SCHEDULE — scheduling records; auto-layout with SCHEDULE_DATE, SCHEDULE_TIME, SCHEDULE_QUANTITY, SCHEDULE_STATUS fields; SCHEDULE_STATUS options: scheduled, rescheduled, canceled, show, noshow, done 10 = NPS — NPS/survey records; auto-layout with RATING, TEXT (feedback), STRING (category) fields 999 = CUSTOM — user-defined custom entity (default for create) Types 2, 3, 4 can be bootstrapped from server-side templates by passing template: <type> in the create payload. DELETE GUARD delete is BLOCKED (returns an error) if the entity has any active Items (Items.status:true) associated with it. You must delete or archive all items first. PERMISSIONS ENFORCEMENT delete checks permissions.write — the caller's permissionsGroups must intersect permissions.write, or permissions.write must be empty. list automatically filters by auth.units (when the caller is unit-scoped) and by auth.entities (entity-level access list from the token). RELATIONSHIPS Group 1 ── N Entity.groupId Account 1 ── N Entity.accountId Entity 1 ── N Items — the records (rows) that conform to this entity's schema; see smarttalks_items Entity.layout[].fields[].foreignEntityId → Entity — cross-entity reference for OBJECT_ID / OBJECT_ID_MULTIPLE fields Entity N ── M PermissionsGroups via permissions.read[] / permissions.write[] (smarttalks_permissions_groups) Entity N ── M Units via units[] (smarttalks_units) ACTIONS get { id } — fetch one entity (full document including layout) list { query, project?, options? } — Mongo filter; server always injects groupId + accountId + status:true. Common filters: { type: 1 } — all CONTACT entities { type: 999 } — all custom entities { isSystem: true } — system-seeded entities only { folder: "<name>" } — entities in a folder { "permissions.read": "<pgId>" } — entities readable by a permissionsGroup Default project (when none given): units, createdAt, icon, title, description, showUnits, showClient, showResponsible, showTags, permissions, layout options supports { limit, skip, sort } create { payload } — required: title. Optional: type (default 999), description, folder, layout[], permissions, units[], settings. Shortcut: include template: <2|3|4|5|10> to bootstrap from a server-side template (sets layout automatically). groupId/accountId are server-injected — do NOT include. Do NOT set isSystem: true. update { id, payload } — mutate any mutable field. groupId and accountId are IMMUTABLE — do NOT include. To replace the full layout[], send the complete layout array (merge semantics: $set on the whole field). _id and __v are stripped server-side before the update. delete { id } — soft-delete (sets status:false). BLOCKED if any active Items exist for this entity.

smarttalks_filters

ChatGPT
Filters — SAVED VIEWS / AUDIENCE CRITERIA. A filter captures a Mongo-ish query DSL against one of the record spaces (interactions, CRM, ERM, SDM) plus UI metadata used to rebuild the filter-builder UX. Saved filters power two use cases: 1. Saved views in the App (users return to the same filtered list of interactions / contacts). 2. Campaign targeting (a campaign references a filter by id+type; at execution time the campaign service fetches the filter and inlines filter.query to resolve the audience). INPUT SHAPE { action, type, ...args } TYPE (REQUIRED on every action) — discriminates which Hermes record space this filter targets: "interactions" — filter over interaction records (open/finished conversations) "crm" — filter over contacts (CRM Persons) "erm" — filter over ERM entities "sdm" — filter over SDM entities IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters status:true by default createdAt, updatedAt (Date) createdBy, updatedBy, deletedBy (ObjectId, refs Users) FIELDS label (string, REQUIRED) — human-readable display name shown in the UI / campaign picker type (string, REQUIRED) — filter-space discriminator (same as the TYPE path param above) query (object) — Mongo-shaped DSL ($and, $or, $in, $gte, $lte, $ne, $regex, etc.). THIS is what gets passed to the target collection at execution time. Example: { "$and": [ { "isActive": true }, { "activeChannel": { "$in": ["whatsapp", "messenger"] } } ] } queryTypes (object), queryComparator (object), filters (array) — UI metadata that the filter-builder component uses to rebuild its rows on open. Opaque to the server. Agents editing a saved filter should preserve these as-is unless they also fully rebuild the UX payload. private (boolean, default true) — only visible to the owner; false = shared in-account share[] (array) — specific users/units the filter is shared with (when private=true and shares are configured) entityId (ObjectId, optional) — anchoring entity reference contactQuery (object), contactEntityId (object), entityTitle (string) — cross-space linking metadata RELATIONSHIPS Group 1 ── N Filter.groupId Account 1 ── N Filter.accountId Filter → referenced by campaigns via { filterId, filterType } — see Campaigns model. Filter.query is inlined at campaign execution time into the target interactions list query. ACTIONS list { type, query?, project?, options? } POST /v1/filters/:type — Mongo filter against saved filters themselves. Common: { label: { $regex: "churn", $options: "i" } } — name search { private: false } — shared filters { createdBy: "<userId>" } — filters I authored options supports { limit, skip, sort }. get { type, id } GET /v1/filters/:type/:id — full saved filter document including query DSL and UI metadata. create { type, payload } PUT /v1/filters/:type — Required: label, type, query. Optional: queryTypes, queryComparator, filters (UI metadata), private, share[], entityId, contactQuery, contactEntityId, entityTitle. groupId + accountId are server-injected from the JWT. update { type, id, payload, project? } PATCH /v1/filters/:type/:id — patch any mutable field. Only updates rows where status:true. delete { type, id } DELETE /v1/filters/:type/:id — soft delete (sets status:false). getCampaign { type, id } GET /v1/filters/:type/:id/campaign — campaign-focused fetch. Use this (rather than get) when you're resolving a filter referenced BY a campaign, since the campaign may rely on specific server-side shaping of the returned filter document. EXAMPLES list { type: "interactions", query: { private: false }, options: { limit: 50 } } get { type: "interactions", id: "<filterId>" } create { type: "interactions", payload: { label: "Open whatsapp today", type: "interactions", query: { "$and": [ { "isActive": true }, { "activeChannel": "whatsapp" }, { "createdAt": { "$gte": "2026-04-20T00:00:00Z" } } ] }, private: false } } update { type: "crm", id: "<filterId>", payload: { label: "Churn risk — VIP" } } delete { type: "interactions", id: "<filterId>" } getCampaign { type: "interactions", id: "<filterId>" }

smarttalks_filters_manage

ChatGPT
Filters — SAVED VIEWS / AUDIENCE CRITERIA. A filter captures a Mongo-ish query DSL against one of the record spaces (interactions, CRM, ERM, SDM) plus UI metadata used to rebuild the filter-builder UX. Saved filters power two use cases: 1. Saved views in the App (users return to the same filtered list of interactions / contacts). 2. Campaign targeting (a campaign references a filter by id+type; at execution time the campaign service fetches the filter and inlines filter.query to resolve the audience). INPUT SHAPE { action, type, ...args } TYPE (REQUIRED on every action) — discriminates which Hermes record space this filter targets: "interactions" — filter over interaction records (open/finished conversations) "crm" — filter over contacts (CRM Persons) "erm" — filter over ERM entities "sdm" — filter over SDM entities IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters status:true by default createdAt, updatedAt (Date) createdBy, updatedBy, deletedBy (ObjectId, refs Users) FIELDS label (string, REQUIRED) — human-readable display name shown in the UI / campaign picker type (string, REQUIRED) — filter-space discriminator (same as the TYPE path param above) query (object) — Mongo-shaped DSL ($and, $or, $in, $gte, $lte, $ne, $regex, etc.). THIS is what gets passed to the target collection at execution time. Example: { "$and": [ { "isActive": true }, { "activeChannel": { "$in": ["whatsapp", "messenger"] } } ] } queryTypes (object), queryComparator (object), filters (array) — UI metadata that the filter-builder component uses to rebuild its rows on open. Opaque to the server. Agents editing a saved filter should preserve these as-is unless they also fully rebuild the UX payload. private (boolean, default true) — only visible to the owner; false = shared in-account share[] (array) — specific users/units the filter is shared with (when private=true and shares are configured) entityId (ObjectId, optional) — anchoring entity reference contactQuery (object), contactEntityId (object), entityTitle (string) — cross-space linking metadata RELATIONSHIPS Group 1 ── N Filter.groupId Account 1 ── N Filter.accountId Filter → referenced by campaigns via { filterId, filterType } — see Campaigns model. Filter.query is inlined at campaign execution time into the target interactions list query. ACTIONS list { type, query?, project?, options? } POST /v1/filters/:type — Mongo filter against saved filters themselves. Common: { label: { $regex: "churn", $options: "i" } } — name search { private: false } — shared filters { createdBy: "<userId>" } — filters I authored options supports { limit, skip, sort }. get { type, id } GET /v1/filters/:type/:id — full saved filter document including query DSL and UI metadata. create { type, payload } PUT /v1/filters/:type — Required: label, type, query. Optional: queryTypes, queryComparator, filters (UI metadata), private, share[], entityId, contactQuery, contactEntityId, entityTitle. groupId + accountId are server-injected from the JWT. update { type, id, payload, project? } PATCH /v1/filters/:type/:id — patch any mutable field. Only updates rows where status:true. delete { type, id } DELETE /v1/filters/:type/:id — soft delete (sets status:false). getCampaign { type, id } GET /v1/filters/:type/:id/campaign — campaign-focused fetch. Use this (rather than get) when you're resolving a filter referenced BY a campaign, since the campaign may rely on specific server-side shaping of the returned filter document. EXAMPLES list { type: "interactions", query: { private: false }, options: { limit: 50 } } get { type: "interactions", id: "<filterId>" } create { type: "interactions", payload: { label: "Open whatsapp today", type: "interactions", query: { "$and": [ { "isActive": true }, { "activeChannel": "whatsapp" }, { "createdAt": { "$gte": "2026-04-20T00:00:00Z" } } ] }, private: false } } update { type: "crm", id: "<filterId>", payload: { label: "Churn risk — VIP" } } delete { type: "interactions", id: "<filterId>" } getCampaign { type: "interactions", id: "<filterId>" }

smarttalks_flows

ChatGPT
Flows — CONVERSATIONAL FLOW DEFINITIONS executed by the inbound runtime (engine/Engine.js) when a message hits a Channel. A Flow is a collection of BLOCKS; each Block has an ordered list of CONTEXTS. At runtime the engine walks a (blockId, contextIndex) tuple: contexts fire sequentially, and navigation between blocks is driven by addon options (button/list/hashtag jumps), not by any "next pointer" on the block itself. Scoped to one Group + Account. INPUT SHAPE { resource, action, ...args } RESOURCES flow — the whole flow document block — a single block nested inside a flow (addressed by flowId + blockId) IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment, shares the flows counter sequence) groupId (ObjectId, REQUIRED, server-injected) accountId (ObjectId, REQUIRED, server-injected) status (boolean, default true) — soft-delete; pre-find middleware filters to status:true createdAt, updatedAt (Date) All changes captured in the Audits collection via a change-stream watch. FLOW FIELDS label (string, REQUIRED) caption (string, default "") isActive (boolean, default false) — flow enabled for runtime use channelType (enum, REQUIRED): "messenger" | "dialog360" | "instagram" | "widget" — NOTE: no separate "whatsapp" value; whatsapp traffic (gupshup, dialog360, whatsappevo, whatsappweb) all route through channelType "dialog360" initBlock (ObjectId) — entry block. If null, engine falls back to blocks[0]._id. finishBlock (ObjectId) — terminal block id the UI marks as end-of-flow. expiration { — inactivity timeout while the user is in this flow time (number, MINUTES; default 10080 = 7d in UI), finishOnExpiration (boolean) — if true, end the interaction on timeout jumpTo (ObjectId) — required when finishOnExpiration=false; block to jump to on timeout } livechatExpiration (number, MINUTES) — independent timeout after handoff to a live attendant globalActions — pre-execution filters + error-fallback cascade (see GLOBAL ACTIONS below) blocks[] — see BLOCKS below BLOCKS (nested: blocks[]) _id (ObjectId), label (REQUIRED), caption, tags[] (UI-only, for builder search) type (enum): "standard" | "gpt" — "gpt" is experimental; most flows are "standard". contexts[] — sequential message+addon+connector chain; engine walks these by index: { message (string), — text rendered to the user sleep (number, milliseconds), — delay before rendering addonId (ObjectId, ref smarttalks_addons), — invoked inline; drives validation / branching connectorId (ObjectId, ref smarttalks_connectors), — outbound HTTP fired inline; result lands in refs.connectors[<id>] prependScript, appendScript (string) — sandboxed JS evaluated around the context } gpt { stages[] { label, caption, isModel, addonId, modelId (ref smarttalks_skills), outputType ("static"|"gpt"), staticOutput, promptOutput, errorMessage } } IMPORTANT: there is NO blocks[].defaultNext or similar pointer. Block-to-block transitions are ALWAYS driven by: • an addon option with blockId set (button/list/plain_list rows) • a hashtag match in globalActions.hashtags[] • expiration.jumpTo firing • errorCount / smartContent / smartFaq jump branches • addon option with jumpType: "queue" (hands off to a queue) SCRIPT HELPER FUNCTIONS (available inside prependScript and appendScript) Each script is wrapped by the engine in an (async () => { try { ... } catch (e) { log } })() IIFE — scripts may use await, and thrown exceptions are caught + logged without halting the flow. Inside the sandbox the engine exposes a set of $-prefixed helpers plus a few read-only globals. Access to refs/HTTP is unrestricted — there is NO capability isolation. READ-ONLY GLOBALS refs — { interaction, blocks, flows, crm, connectors, evidences, variables, addons } contact / client— current contact document (CRM Person) channel — the Channel this interaction entered through entity, contactEntity — entity/contact-entity definitions blockId — current block id (mutable via $blockJump; do NOT reassign directly) contextIndex — current context index inboundEvent — the raw inbound message event metadata — inboundEvent.metadata wa_ — WhatsApp-specific inbound fields when on a WA channel HELPERS — READ FROM REFS (sync) $contactId() → current contact _id (string) $interactionId() → current interaction _id $interaction() → full interaction document $getContactName() → inboundEvent.sender.name $lastInput() → the treated text of the user's last message $metadata(key?) → inboundEvent.metadata[key] or the whole metadata object $crm(fieldId) → value of a CRM field for the current contact $evidence(addonId) → evidence captured by an addon $connector(connectorId) → cached connector response (same slot $fetchConnector writes to) $units() → unit ids attached to the interaction $envVariable(variableId) → value of an environment variable HELPERS — WRITE REFS + PERSIST (async — always `await`) $saveContactField(fieldId, value) → PUT to CRM; updates refs.crm $saveEvidence(addonId, message) → appends evidence; persists to interaction $saveConnector(connectorId, value) → writes into refs.connectors[<id>] + interaction doc (use when you've synthesised a value without calling an HTTP connector) $saveUnit(unitId) → appends to interaction.units $saveInteractionName(name) → sets interaction.display.label $saveTag(tagId) → PUT to hermes; tags the interaction $updateEnvVariable(variableId, value) → persists + updates refs.variables.environment $setAttendant(attendantId) → assigns an attendant (handoff-ish) $pushInternalMessage(message) → posts an internal/admin message to the interaction HELPERS — EXTERNAL CALLS (async) $fetch(method, url, body?, headers?) → generic axios passthrough; returns { status, data } $fetchConnector(connectorId) → fetch a Connector by id, run its template + request, cache result in refs.connectors[<connectorId>] + the interaction doc (same slot as inline connectorId on a context) $integration(method, route, body?, headers?) → call the internal integrations service $completion(prompt, input, modelId, messages?) → LLM completion HELPERS — SDM (CRM entities) $searchSDM(body) → search across all entities $searchSDMItems(entityId, body) → search items in one entity $readSDMItem(entityId, itemId, body?) → read one item $readSDMItemLabel(entityId, itemId) → item label only $addSDMItem(entityId, body) → create item $updateSDMItemField(entityId, itemId, fieldId, body) → patch by fieldId $updateSDMItemFieldByType(entityId, itemId, fieldType, body)→ patch by field type $updateSDMItemData(entityId, itemId, body) → patch item data HELPERS — FLOW CONTROL / UX $blockJump(newBlockId) → jump to a different block, resets contextIndex=0, persists the new position; use instead of reassigning `blockId` directly $overrideContextMessage(msg) → replace the current context's rendered message (sync — apply in prependScript) $overrideList(options, title?) → rewrite a list addon's options/title dynamically (async) $formatDate(date, format) → moment-backed formatter $messageHistory() → full message history for the current interaction $availableUnits() → units the caller/user can see $availableAttendants(unitId, statuses?) → online attendants filtered by status TYPICAL USAGE prependScript: await $saveContactField("<fieldId>", $lastInput()); appendScript: if (!$crm("<fieldId>")) await $blockJump("<fallbackBlockId>"); ERROR SEMANTICS - Script-level exceptions are caught + console-logged with { flowId, blockId, contactId, interactionId }; the flow CONTINUES to the next step regardless. - Individual helpers catch their own errors and return undefined / error-shaped objects; check return values if you branch on success. GLOBAL ACTIONS (pre-empt normal block execution; evaluated in this order) 1. `isAudioInterpreterActive` (boolean) — if the inbound message is audio, transcribe → replace message text BEFORE hashtag/FAQ matching. 2. `hashtags[]` — [{ label, jumpType: "block"|"queue", blockId?, queueId? }]; if the user's message contains a matching hashtag, jump immediately (reset contextIndex=0) or queue. 3. ERROR-FALLBACK CASCADE (fires when the current addon rejects input): 3a. addon-level `errorCount` (on the Addon doc) — takes priority. 3b. flow-level `errorCount`: { isActive, limit (number), jumpType: "block"|"queue"|"continue", blockId?, queueId? } 3c. `smartContent`: { isActive, shouldValidate, modelId (ref smarttalks_skills — LLM), jumpType: "block"|"queue"|"continue", blockId?, queueId?, exceptions[] } 3d. `smartFaq`: { isActive, modelId, jumpType: "block"|"queue"|"continue", blockId?, queueId? } Global actions DO NOT fire once the interaction is handed off to a live attendant (isLive=true). RUNTIME BEHAVIOR (Engine.js) Entry: inbound message + channel.flowId → engine loads the flow + all blocks into `refs`. Starting point = flow.initBlock ?? blocks[0]._id. Walking: engine advances through the current block's contexts by index; each context renders its `message` (after substitution), sleeps, invokes `addonId` / `connectorId` if present, runs prepend/appendScript, then either advances contextIndex OR jumps via one of the triggers above. Outputs: Addon outputs → `refs.addons[<addonId>]`; connector responses → `refs.connectors[<connectorId>]` (also written to the interaction document). Downstream templates can reference via `$.addons.<id>` / `$.connectors.<id>` (see smarttalks_connectors for the template DSL). Finish: triggered by expiration with no jumpTo, by an addon of type "finish", or by the engine at interaction boundaries (stale > 24h). Handoff: addon option with `jumpType="queue"` or globalActions queue branch → InteractionProvider.assign() to the target queue; interaction flips isLive=true, the flow runtime disengages. CHANNEL BINDING A Channel's `flowId` points at ONE default Flow (1:1). Channel `menus[]` can bind deep-link entries to alternative { flowId, blockId } combos. The gupshup webhook (routes/webhooks/gupshup.js) loads `channel.flowId` and dispatches the inbound message to the engine with that flow as the entry. IMPORT / EXPORT / DUPLICATE — how they differ export GET /v1/flows/:id/export Returns a portable blob: { flow, addons[], connectors[] }. ALL ObjectId cross-refs (addonId, connectorId, initBlock, finishBlock, globalActions..blockId, expiration.jumpTo, addon options[].blockId) are REMAPPED to 0-based array INDICES so the document is account-portable. import PUT /v1/flows/import Takes the exported blob (unwrapped — request body IS the blob, not { payload }) and creates fresh addons + connectors + the flow, remapping indices back to new ObjectIds within the caller's account. duplicate PUT /v1/flows/:id/duplicate In-account deep clone — copies blocks + contexts. Accepts metadata overrides in body: { label?, caption?, channelType? }. Sets warning=true on blocks with unresolved internal references. RELATIONSHIPS Group 1 ── N Flow.groupId Account 1 ── N Flow.accountId Channel 1 ── 1 Flow via channelId.flowId (and via channel.menus[].flowId for deep-link menu entries) Flow.blocks[].contexts[].addonId → smarttalks_addons Flow.blocks[].contexts[].connectorId → smarttalks_connectors Flow.blocks[].gpt.stages[].modelId → smarttalks_skills (LLM model bindings) globalActions.smartFaq.modelId / smartContent.modelId → smarttalks_skills Addon option jumps, hashtags, and error fallbacks target block ids WITHIN the same Flow; queue jumps target smarttalks_queues. ACTIONS flow.list { query?, project?, options? } POST /v1/flows — Mongo-style read. Common: { isActive: true, channelType: "dialog360" } { label: { $regex: "onboarding", $options: "i" } } { "globalActions.isAudioInterpreterActive": true } flow.get { id } GET /v1/flows/:id — full nested document (all blocks + contexts). flow.create { payload } PUT /v1/flows — Required: label, channelType. Typical: start empty, then add blocks/initBlock/finishBlock via flow.update or block.update. flow.update { id, payload, project? } PATCH /v1/flows/:id — patch mutable fields. For heavy block rewrites use flow.import instead. flow.delete { id } DELETE /v1/flows/:id — soft-delete. flow.import { payload } PUT /v1/flows/import — payload IS the blob (NOT { payload: blob }); pair with flow.export for cross-environment moves. flow.export { id } GET /v1/flows/:id/export — portable blob with indices. flow.duplicate { id, payload? } PUT /v1/flows/:id/duplicate — clone within account; payload may override { label, caption, channelType }. block.get { id, blockId } GET /v1/flows/:id/blocks/:blockId — one block. block.update { id, blockId, payload } PATCH /v1/flows/:id/blocks/:blockId — patch a single block (label/caption/contexts[]/gpt) without rewriting the flow. EXAMPLES flow.list { resource: "flow", action: "list", query: { isActive: true, channelType: "dialog360" } } flow.get { resource: "flow", action: "get", id: "<flowId>" } flow.create { resource: "flow", action: "create", payload: { label: "Onboarding", channelType: "widget" } } flow.duplicate{ resource: "flow", action: "duplicate", id: "<srcId>", payload: { label: "Onboarding (staging copy)" } } flow.import { resource: "flow", action: "import", payload: <exported flow blob> } block.update { resource: "block", action: "update", id: "<flowId>", blockId: "<bId>", payload: { label: "Greeting v2", contexts: [ { message: "Hi $.contact.firstName!", sleep: 500 }, { addonId: "<intentAddonId>" } ] } }

smarttalks_flows_manage

ChatGPT
Flows — CONVERSATIONAL FLOW DEFINITIONS executed by the inbound runtime (engine/Engine.js) when a message hits a Channel. A Flow is a collection of BLOCKS; each Block has an ordered list of CONTEXTS. At runtime the engine walks a (blockId, contextIndex) tuple: contexts fire sequentially, and navigation between blocks is driven by addon options (button/list/hashtag jumps), not by any "next pointer" on the block itself. Scoped to one Group + Account. INPUT SHAPE { resource, action, ...args } RESOURCES flow — the whole flow document block — a single block nested inside a flow (addressed by flowId + blockId) IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment, shares the flows counter sequence) groupId (ObjectId, REQUIRED, server-injected) accountId (ObjectId, REQUIRED, server-injected) status (boolean, default true) — soft-delete; pre-find middleware filters to status:true createdAt, updatedAt (Date) All changes captured in the Audits collection via a change-stream watch. FLOW FIELDS label (string, REQUIRED) caption (string, default "") isActive (boolean, default false) — flow enabled for runtime use channelType (enum, REQUIRED): "messenger" | "dialog360" | "instagram" | "widget" — NOTE: no separate "whatsapp" value; whatsapp traffic (gupshup, dialog360, whatsappevo, whatsappweb) all route through channelType "dialog360" initBlock (ObjectId) — entry block. If null, engine falls back to blocks[0]._id. finishBlock (ObjectId) — terminal block id the UI marks as end-of-flow. expiration { — inactivity timeout while the user is in this flow time (number, MINUTES; default 10080 = 7d in UI), finishOnExpiration (boolean) — if true, end the interaction on timeout jumpTo (ObjectId) — required when finishOnExpiration=false; block to jump to on timeout } livechatExpiration (number, MINUTES) — independent timeout after handoff to a live attendant globalActions — pre-execution filters + error-fallback cascade (see GLOBAL ACTIONS below) blocks[] — see BLOCKS below BLOCKS (nested: blocks[]) _id (ObjectId), label (REQUIRED), caption, tags[] (UI-only, for builder search) type (enum): "standard" | "gpt" — "gpt" is experimental; most flows are "standard". contexts[] — sequential message+addon+connector chain; engine walks these by index: { message (string), — text rendered to the user sleep (number, milliseconds), — delay before rendering addonId (ObjectId, ref smarttalks_addons), — invoked inline; drives validation / branching connectorId (ObjectId, ref smarttalks_connectors), — outbound HTTP fired inline; result lands in refs.connectors[<id>] prependScript, appendScript (string) — sandboxed JS evaluated around the context } gpt { stages[] { label, caption, isModel, addonId, modelId (ref smarttalks_skills), outputType ("static"|"gpt"), staticOutput, promptOutput, errorMessage } } IMPORTANT: there is NO blocks[].defaultNext or similar pointer. Block-to-block transitions are ALWAYS driven by: • an addon option with blockId set (button/list/plain_list rows) • a hashtag match in globalActions.hashtags[] • expiration.jumpTo firing • errorCount / smartContent / smartFaq jump branches • addon option with jumpType: "queue" (hands off to a queue) SCRIPT HELPER FUNCTIONS (available inside prependScript and appendScript) Each script is wrapped by the engine in an (async () => { try { ... } catch (e) { log } })() IIFE — scripts may use await, and thrown exceptions are caught + logged without halting the flow. Inside the sandbox the engine exposes a set of $-prefixed helpers plus a few read-only globals. Access to refs/HTTP is unrestricted — there is NO capability isolation. READ-ONLY GLOBALS refs — { interaction, blocks, flows, crm, connectors, evidences, variables, addons } contact / client— current contact document (CRM Person) channel — the Channel this interaction entered through entity, contactEntity — entity/contact-entity definitions blockId — current block id (mutable via $blockJump; do NOT reassign directly) contextIndex — current context index inboundEvent — the raw inbound message event metadata — inboundEvent.metadata wa_ — WhatsApp-specific inbound fields when on a WA channel HELPERS — READ FROM REFS (sync) $contactId() → current contact _id (string) $interactionId() → current interaction _id $interaction() → full interaction document $getContactName() → inboundEvent.sender.name $lastInput() → the treated text of the user's last message $metadata(key?) → inboundEvent.metadata[key] or the whole metadata object $crm(fieldId) → value of a CRM field for the current contact $evidence(addonId) → evidence captured by an addon $connector(connectorId) → cached connector response (same slot $fetchConnector writes to) $units() → unit ids attached to the interaction $envVariable(variableId) → value of an environment variable HELPERS — WRITE REFS + PERSIST (async — always `await`) $saveContactField(fieldId, value) → PUT to CRM; updates refs.crm $saveEvidence(addonId, message) → appends evidence; persists to interaction $saveConnector(connectorId, value) → writes into refs.connectors[<id>] + interaction doc (use when you've synthesised a value without calling an HTTP connector) $saveUnit(unitId) → appends to interaction.units $saveInteractionName(name) → sets interaction.display.label $saveTag(tagId) → PUT to hermes; tags the interaction $updateEnvVariable(variableId, value) → persists + updates refs.variables.environment $setAttendant(attendantId) → assigns an attendant (handoff-ish) $pushInternalMessage(message) → posts an internal/admin message to the interaction HELPERS — EXTERNAL CALLS (async) $fetch(method, url, body?, headers?) → generic axios passthrough; returns { status, data } $fetchConnector(connectorId) → fetch a Connector by id, run its template + request, cache result in refs.connectors[<connectorId>] + the interaction doc (same slot as inline connectorId on a context) $integration(method, route, body?, headers?) → call the internal integrations service $completion(prompt, input, modelId, messages?) → LLM completion HELPERS — SDM (CRM entities) $searchSDM(body) → search across all entities $searchSDMItems(entityId, body) → search items in one entity $readSDMItem(entityId, itemId, body?) → read one item $readSDMItemLabel(entityId, itemId) → item label only $addSDMItem(entityId, body) → create item $updateSDMItemField(entityId, itemId, fieldId, body) → patch by fieldId $updateSDMItemFieldByType(entityId, itemId, fieldType, body)→ patch by field type $updateSDMItemData(entityId, itemId, body) → patch item data HELPERS — FLOW CONTROL / UX $blockJump(newBlockId) → jump to a different block, resets contextIndex=0, persists the new position; use instead of reassigning `blockId` directly $overrideContextMessage(msg) → replace the current context's rendered message (sync — apply in prependScript) $overrideList(options, title?) → rewrite a list addon's options/title dynamically (async) $formatDate(date, format) → moment-backed formatter $messageHistory() → full message history for the current interaction $availableUnits() → units the caller/user can see $availableAttendants(unitId, statuses?) → online attendants filtered by status TYPICAL USAGE prependScript: await $saveContactField("<fieldId>", $lastInput()); appendScript: if (!$crm("<fieldId>")) await $blockJump("<fallbackBlockId>"); ERROR SEMANTICS - Script-level exceptions are caught + console-logged with { flowId, blockId, contactId, interactionId }; the flow CONTINUES to the next step regardless. - Individual helpers catch their own errors and return undefined / error-shaped objects; check return values if you branch on success. GLOBAL ACTIONS (pre-empt normal block execution; evaluated in this order) 1. `isAudioInterpreterActive` (boolean) — if the inbound message is audio, transcribe → replace message text BEFORE hashtag/FAQ matching. 2. `hashtags[]` — [{ label, jumpType: "block"|"queue", blockId?, queueId? }]; if the user's message contains a matching hashtag, jump immediately (reset contextIndex=0) or queue. 3. ERROR-FALLBACK CASCADE (fires when the current addon rejects input): 3a. addon-level `errorCount` (on the Addon doc) — takes priority. 3b. flow-level `errorCount`: { isActive, limit (number), jumpType: "block"|"queue"|"continue", blockId?, queueId? } 3c. `smartContent`: { isActive, shouldValidate, modelId (ref smarttalks_skills — LLM), jumpType: "block"|"queue"|"continue", blockId?, queueId?, exceptions[] } 3d. `smartFaq`: { isActive, modelId, jumpType: "block"|"queue"|"continue", blockId?, queueId? } Global actions DO NOT fire once the interaction is handed off to a live attendant (isLive=true). RUNTIME BEHAVIOR (Engine.js) Entry: inbound message + channel.flowId → engine loads the flow + all blocks into `refs`. Starting point = flow.initBlock ?? blocks[0]._id. Walking: engine advances through the current block's contexts by index; each context renders its `message` (after substitution), sleeps, invokes `addonId` / `connectorId` if present, runs prepend/appendScript, then either advances contextIndex OR jumps via one of the triggers above. Outputs: Addon outputs → `refs.addons[<addonId>]`; connector responses → `refs.connectors[<connectorId>]` (also written to the interaction document). Downstream templates can reference via `$.addons.<id>` / `$.connectors.<id>` (see smarttalks_connectors for the template DSL). Finish: triggered by expiration with no jumpTo, by an addon of type "finish", or by the engine at interaction boundaries (stale > 24h). Handoff: addon option with `jumpType="queue"` or globalActions queue branch → InteractionProvider.assign() to the target queue; interaction flips isLive=true, the flow runtime disengages. CHANNEL BINDING A Channel's `flowId` points at ONE default Flow (1:1). Channel `menus[]` can bind deep-link entries to alternative { flowId, blockId } combos. The gupshup webhook (routes/webhooks/gupshup.js) loads `channel.flowId` and dispatches the inbound message to the engine with that flow as the entry. IMPORT / EXPORT / DUPLICATE — how they differ export GET /v1/flows/:id/export Returns a portable blob: { flow, addons[], connectors[] }. ALL ObjectId cross-refs (addonId, connectorId, initBlock, finishBlock, globalActions..blockId, expiration.jumpTo, addon options[].blockId) are REMAPPED to 0-based array INDICES so the document is account-portable. import PUT /v1/flows/import Takes the exported blob (unwrapped — request body IS the blob, not { payload }) and creates fresh addons + connectors + the flow, remapping indices back to new ObjectIds within the caller's account. duplicate PUT /v1/flows/:id/duplicate In-account deep clone — copies blocks + contexts. Accepts metadata overrides in body: { label?, caption?, channelType? }. Sets warning=true on blocks with unresolved internal references. RELATIONSHIPS Group 1 ── N Flow.groupId Account 1 ── N Flow.accountId Channel 1 ── 1 Flow via channelId.flowId (and via channel.menus[].flowId for deep-link menu entries) Flow.blocks[].contexts[].addonId → smarttalks_addons Flow.blocks[].contexts[].connectorId → smarttalks_connectors Flow.blocks[].gpt.stages[].modelId → smarttalks_skills (LLM model bindings) globalActions.smartFaq.modelId / smartContent.modelId → smarttalks_skills Addon option jumps, hashtags, and error fallbacks target block ids WITHIN the same Flow; queue jumps target smarttalks_queues. ACTIONS flow.list { query?, project?, options? } POST /v1/flows — Mongo-style read. Common: { isActive: true, channelType: "dialog360" } { label: { $regex: "onboarding", $options: "i" } } { "globalActions.isAudioInterpreterActive": true } flow.get { id } GET /v1/flows/:id — full nested document (all blocks + contexts). flow.create { payload } PUT /v1/flows — Required: label, channelType. Typical: start empty, then add blocks/initBlock/finishBlock via flow.update or block.update. flow.update { id, payload, project? } PATCH /v1/flows/:id — patch mutable fields. For heavy block rewrites use flow.import instead. flow.delete { id } DELETE /v1/flows/:id — soft-delete. flow.import { payload } PUT /v1/flows/import — payload IS the blob (NOT { payload: blob }); pair with flow.export for cross-environment moves. flow.export { id } GET /v1/flows/:id/export — portable blob with indices. flow.duplicate { id, payload? } PUT /v1/flows/:id/duplicate — clone within account; payload may override { label, caption, channelType }. block.get { id, blockId } GET /v1/flows/:id/blocks/:blockId — one block. block.update { id, blockId, payload } PATCH /v1/flows/:id/blocks/:blockId — patch a single block (label/caption/contexts[]/gpt) without rewriting the flow. EXAMPLES flow.list { resource: "flow", action: "list", query: { isActive: true, channelType: "dialog360" } } flow.get { resource: "flow", action: "get", id: "<flowId>" } flow.create { resource: "flow", action: "create", payload: { label: "Onboarding", channelType: "widget" } } flow.duplicate{ resource: "flow", action: "duplicate", id: "<srcId>", payload: { label: "Onboarding (staging copy)" } } flow.import { resource: "flow", action: "import", payload: <exported flow blob> } block.update { resource: "block", action: "update", id: "<flowId>", blockId: "<bId>", payload: { label: "Greeting v2", contexts: [ { message: "Hi $.contact.firstName!", sleep: 500 }, { addonId: "<intentAddonId>" } ] } }

smarttalks_forms

ChatGPT
Forms — DATA CAPTURE FORMS that accept public or internal submissions. A form belongs to ONE Group and ONE Account. Forms can be linked to a CRM Entity so that submissions create or update Items. Two structural types are supported: REGULAR (single-page) and STEPS (multi-step wizard). Each form has a unique auto-generated slug used for the public submission URL. INPUT SHAPE { resource, action, ...args } IDENTITY & TENANCY (applies to every form record) _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null, default null) — soft-delete; list queries filter deletedAt:null by default createdBy, updatedBy, deletedBy (ObjectId — User) CORE FIELDS title (string, required, min 3 chars) — display name of the form description (string) — optional subtitle shown to submitters avatar (string, URL) — image shown at the top of the form type (enum, required, default "REGULAR"): "REGULAR" | "STEPS" REGULAR — single-page form; all steps[0].fields rendered on one screen STEPS — multi-step wizard; each step is a separate screen with a progress indicator isActive (boolean, default false) — controls public accessibility; set true to accept submissions slug (string, unique, auto-generated 7-char nanoid) — public URL segment; server-managed, do NOT set manually redirectURL (string, URL) — where to send the submitter after successful submission ENTITY INTEGRATION entityId (ObjectId → Entity) — when set, each submission creates or updates an Item in that entity denominations[] — field mapping from form to entity: { formFieldId (string) — the field.id within steps[].fields, entityFieldId (ObjectId → Entity.layout[].fields[]._id) } NESTED: STEPS (array — always present, even for REGULAR forms which use a single step) Each step: title (string, min 3 chars on create) — step heading description (string) — step subtitle icon (string) — icon name step (number, int, positive) — 1-based position; keep sequential fields[] — the inputs shown on this step (see FIELD SCHEMA below) FIELD SCHEMA (steps[].fields[]) id (string, auto-generated 12-char nanoid) — stable client-side reference; maps to denominations[].formFieldId label (string, required) type (enum, required, default "short_text"): "short_text" | "long_text" | "number" | "checkbox" | "checkbox_group" "radio" | "select" | "multi_select" | "email" | "multi_email" "url" | "phone" | "cpf" | "date" | "time" "text_info" | "media_info" | "rating" "welcome" | "thankyou" | "pharagraph" placeholder (string) — input hint text options (string[]) — static choices for select/radio/checkbox_group/multi_select required (boolean, default false) disabled (boolean, default false) min, max (number) — numeric or rating bounds textInfo (string) — body text for text_info fields mediaInfo (string) — media URL for media_info fields htmlContent (string) — HTML body for rich content backgroundColor (string) — CSS color for the field card showTitle (boolean, default true) template (enum): "simple" | "image-top" | "image-bottom" | "image-left" | "image-right" image (string) — image URL for template fields buttonText (string) — CTA label for welcome/thankyou fields size (enum, default "col-12"): "col-6" | "col-12" — grid column width INDEXES (shape queries to hit these efficiently) (groupId, accountId, createdAt) — always auto-scoped by server; no need to include in query RELATIONSHIPS Group 1 ── N Form.groupId Account 1 ── N Form.accountId Entity 1 ── N Form.entityId — when set, submissions create/update Items Form 1 ── N Submission — fetch via smarttalks_submissions tool with formId RESOURCES & ACTIONS form: list { skip?, limit?, sortBy?, sortOrder?, search? } Paginated list of forms for the caller's account. All params are optional. skip (number, default 0), limit (number, default 10) sortBy (string, default "createdAt"), sortOrder ("asc" | "desc", default "desc") search (string) — case-insensitive regex against title, description, and slug Response: { items: Form[], count: number } get { id } Fetch one form by its _id (full document including steps and fields). create { payload } Required: title (min 3), steps (array with at least one step containing fields). Optional: type, description, avatar, isActive, redirectURL, entityId, denominations[]. groupId/accountId are server-injected — do NOT include. Server auto-generates slug and field id values. update { id, payload } Replace mutable fields. Required in payload: title, type (even if unchanged — server validates them). Optional: description, avatar, isActive, redirectURL, entityId, denominations[], steps. groupId/accountId are IMMUTABLE — do NOT include. delete { id } Soft-delete: sets deletedAt = now, hidden from list. EXAMPLES form.list { skip: 0, limit: 20, sortOrder: "desc", search: "onboarding" } form.get { id: "<formId>" } form.create { payload: { title: "Lead Capture", type: "REGULAR", isActive: true, steps: [{ title: "Step 1", step: 1, fields: [{ label: "Name", type: "short_text", required: true }] }] } } form.update { id: "<formId>", payload: { title: "Lead Capture v2", type: "REGULAR", isActive: false } } form.delete { id: "<formId>" }

smarttalks_forms_manage

ChatGPT
Forms — DATA CAPTURE FORMS that accept public or internal submissions. A form belongs to ONE Group and ONE Account. Forms can be linked to a CRM Entity so that submissions create or update Items. Two structural types are supported: REGULAR (single-page) and STEPS (multi-step wizard). Each form has a unique auto-generated slug used for the public submission URL. INPUT SHAPE { resource, action, ...args } IDENTITY & TENANCY (applies to every form record) _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null, default null) — soft-delete; list queries filter deletedAt:null by default createdBy, updatedBy, deletedBy (ObjectId — User) CORE FIELDS title (string, required, min 3 chars) — display name of the form description (string) — optional subtitle shown to submitters avatar (string, URL) — image shown at the top of the form type (enum, required, default "REGULAR"): "REGULAR" | "STEPS" REGULAR — single-page form; all steps[0].fields rendered on one screen STEPS — multi-step wizard; each step is a separate screen with a progress indicator isActive (boolean, default false) — controls public accessibility; set true to accept submissions slug (string, unique, auto-generated 7-char nanoid) — public URL segment; server-managed, do NOT set manually redirectURL (string, URL) — where to send the submitter after successful submission ENTITY INTEGRATION entityId (ObjectId → Entity) — when set, each submission creates or updates an Item in that entity denominations[] — field mapping from form to entity: { formFieldId (string) — the field.id within steps[].fields, entityFieldId (ObjectId → Entity.layout[].fields[]._id) } NESTED: STEPS (array — always present, even for REGULAR forms which use a single step) Each step: title (string, min 3 chars on create) — step heading description (string) — step subtitle icon (string) — icon name step (number, int, positive) — 1-based position; keep sequential fields[] — the inputs shown on this step (see FIELD SCHEMA below) FIELD SCHEMA (steps[].fields[]) id (string, auto-generated 12-char nanoid) — stable client-side reference; maps to denominations[].formFieldId label (string, required) type (enum, required, default "short_text"): "short_text" | "long_text" | "number" | "checkbox" | "checkbox_group" "radio" | "select" | "multi_select" | "email" | "multi_email" "url" | "phone" | "cpf" | "date" | "time" "text_info" | "media_info" | "rating" "welcome" | "thankyou" | "pharagraph" placeholder (string) — input hint text options (string[]) — static choices for select/radio/checkbox_group/multi_select required (boolean, default false) disabled (boolean, default false) min, max (number) — numeric or rating bounds textInfo (string) — body text for text_info fields mediaInfo (string) — media URL for media_info fields htmlContent (string) — HTML body for rich content backgroundColor (string) — CSS color for the field card showTitle (boolean, default true) template (enum): "simple" | "image-top" | "image-bottom" | "image-left" | "image-right" image (string) — image URL for template fields buttonText (string) — CTA label for welcome/thankyou fields size (enum, default "col-12"): "col-6" | "col-12" — grid column width INDEXES (shape queries to hit these efficiently) (groupId, accountId, createdAt) — always auto-scoped by server; no need to include in query RELATIONSHIPS Group 1 ── N Form.groupId Account 1 ── N Form.accountId Entity 1 ── N Form.entityId — when set, submissions create/update Items Form 1 ── N Submission — fetch via smarttalks_submissions tool with formId RESOURCES & ACTIONS form: list { skip?, limit?, sortBy?, sortOrder?, search? } Paginated list of forms for the caller's account. All params are optional. skip (number, default 0), limit (number, default 10) sortBy (string, default "createdAt"), sortOrder ("asc" | "desc", default "desc") search (string) — case-insensitive regex against title, description, and slug Response: { items: Form[], count: number } get { id } Fetch one form by its _id (full document including steps and fields). create { payload } Required: title (min 3), steps (array with at least one step containing fields). Optional: type, description, avatar, isActive, redirectURL, entityId, denominations[]. groupId/accountId are server-injected — do NOT include. Server auto-generates slug and field id values. update { id, payload } Replace mutable fields. Required in payload: title, type (even if unchanged — server validates them). Optional: description, avatar, isActive, redirectURL, entityId, denominations[], steps. groupId/accountId are IMMUTABLE — do NOT include. delete { id } Soft-delete: sets deletedAt = now, hidden from list. EXAMPLES form.list { skip: 0, limit: 20, sortOrder: "desc", search: "onboarding" } form.get { id: "<formId>" } form.create { payload: { title: "Lead Capture", type: "REGULAR", isActive: true, steps: [{ title: "Step 1", step: 1, fields: [{ label: "Name", type: "short_text", required: true }] }] } } form.update { id: "<formId>", payload: { title: "Lead Capture v2", type: "REGULAR", isActive: false } } form.delete { id: "<formId>" }

smarttalks_groups

ChatGPT
Groups, the TOP-LEVEL tenant. A Group is the root organisational unit; it owns one or more Accounts and all other resources (Users, Units, Tags, Tokens, PermissionsGroups) carry a groupId back to it. IDENTITY _id (ObjectId) — primary key name (string, required) — human-readable group label company (string, required) — legal company / organisation name cnpj (string, required) — Brazilian tax registration number (treat as SENSITIVE) CONTACT addresses[] (Array of address objects) — mailing / physical addresses for the group; shape is free-form but typically: { street, number, complement, neighbourhood, city, state, zip, country } BILLING INTEGRATION contaAzulCustomerId (string, default null) — external customer ID in the Conta Azul billing system; treat as SENSITIVE. Null means not yet linked to a billing record. CHILD REFERENCES accounts[] (ObjectId[]) — IDs of every Account that belongs to this group (see smarttalks_accounts). Maintained by pushAccount / pullAccount operations on the server; do NOT mutate directly via update. LIFECYCLE status (boolean, default true) — soft-delete flag. The pre-find Mongoose hook automatically filters status:false records, so list results only ever include active groups. Deactivation is handled by admin flows, not via this MCP tool. createdAt (Date, default Date.now) — record creation timestamp; server-managed. updatedAt (Date, default Date.now) — last-modified timestamp; automatically set by a Mongoose pre-update hook, so it is not necessary to include it in update payloads. NOTE: Every mutation is recorded to the Audits collection via a Mongoose change-stream watcher. RELATIONSHIPS Group 1 ── N Account — Account.groupId → Group._id (see smarttalks_accounts) Group 1 ── N User — User.groupId → Group._id (see smarttalks_users); groupId is IMMUTABLE on User after creation Group 1 ── N Unit — Unit.groupId → Group._id (see smarttalks_units) Group 1 ── N Tag — Tag.groupId → Group._id (see smarttalks_tags) Group 1 ── N Token — Token.groupId → Group._id (see smarttalks_tokens) Group 1 ── N PermissionsGroup — PermissionsGroup.groupId → Group._id (see smarttalks_permissions_groups) ACTIONS get { id } — fetch one group by _id list { query, project?, options? } — Mongo filter against active groups (status:true is implicit). Common shapes: { name: "Acme" } — exact name match { cnpj: "12.345.678/0001-99" } — lookup by CNPJ { contaAzulCustomerId: "<id>" } — find by billing integration ID { accounts: { $in: ["<accountId>"] } } — groups that contain a specific Account options supports { limit, skip, sort } update { id, payload } — mutate any editable field: name, company, cnpj, addresses, contaAzulCustomerId. Do NOT include accounts[] — use dedicated account-management flows. NOT EXPOSED create/delete — Groups are provisioned and deactivated via billing/admin flows outside this MCP tool. There is no create or delete action for smarttalks_groups.

smarttalks_groups_manage

ChatGPT
Groups, the TOP-LEVEL tenant. A Group is the root organisational unit; it owns one or more Accounts and all other resources (Users, Units, Tags, Tokens, PermissionsGroups) carry a groupId back to it. IDENTITY _id (ObjectId) — primary key name (string, required) — human-readable group label company (string, required) — legal company / organisation name cnpj (string, required) — Brazilian tax registration number (treat as SENSITIVE) CONTACT addresses[] (Array of address objects) — mailing / physical addresses for the group; shape is free-form but typically: { street, number, complement, neighbourhood, city, state, zip, country } BILLING INTEGRATION contaAzulCustomerId (string, default null) — external customer ID in the Conta Azul billing system; treat as SENSITIVE. Null means not yet linked to a billing record. CHILD REFERENCES accounts[] (ObjectId[]) — IDs of every Account that belongs to this group (see smarttalks_accounts). Maintained by pushAccount / pullAccount operations on the server; do NOT mutate directly via update. LIFECYCLE status (boolean, default true) — soft-delete flag. The pre-find Mongoose hook automatically filters status:false records, so list results only ever include active groups. Deactivation is handled by admin flows, not via this MCP tool. createdAt (Date, default Date.now) — record creation timestamp; server-managed. updatedAt (Date, default Date.now) — last-modified timestamp; automatically set by a Mongoose pre-update hook, so it is not necessary to include it in update payloads. NOTE: Every mutation is recorded to the Audits collection via a Mongoose change-stream watcher. RELATIONSHIPS Group 1 ── N Account — Account.groupId → Group._id (see smarttalks_accounts) Group 1 ── N User — User.groupId → Group._id (see smarttalks_users); groupId is IMMUTABLE on User after creation Group 1 ── N Unit — Unit.groupId → Group._id (see smarttalks_units) Group 1 ── N Tag — Tag.groupId → Group._id (see smarttalks_tags) Group 1 ── N Token — Token.groupId → Group._id (see smarttalks_tokens) Group 1 ── N PermissionsGroup — PermissionsGroup.groupId → Group._id (see smarttalks_permissions_groups) ACTIONS get { id } — fetch one group by _id list { query, project?, options? } — Mongo filter against active groups (status:true is implicit). Common shapes: { name: "Acme" } — exact name match { cnpj: "12.345.678/0001-99" } — lookup by CNPJ { contaAzulCustomerId: "<id>" } — find by billing integration ID { accounts: { $in: ["<accountId>"] } } — groups that contain a specific Account options supports { limit, skip, sort } update { id, payload } — mutate any editable field: name, company, cnpj, addresses, contaAzulCustomerId. Do NOT include accounts[] — use dedicated account-management flows. NOT EXPOSED create/delete — Groups are provisioned and deactivated via billing/admin flows outside this MCP tool. There is no create or delete action for smarttalks_groups.

smarttalks_interactions

ChatGPT
Interactions — CUSTOMER INTERACTIONS across channels (whatsapp, messenger, instagram, widget, email, nvoip, soulmachines, etc.). An interaction is a single conversation thread between a contact and one or more attendants (users), carrying messages, evidences, tags, logs, and analytics. Scoped to one Group + Account; every read/write is auto-scoped to the caller's JWT claims. INPUT SHAPE { resource, action, ...args } IDENTITY & TENANCY (applies to every interaction record) _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters status:true by default createdAt, updatedAt (Date) — managed automatically CHANNEL & STATE activeChannel (enum, required): "messenger" | "instagram" | "widget" | "whatsapp" | "soulmachines" | "email" | "nvoip" | ... activeChannelId, activeChannelKey (string) — external channel identifiers isActive (boolean) — interaction is open (not finished) isLive (boolean) — currently live (client side connected) isBot (boolean) — handled by bot/flow isGroup (boolean) — group chat isQueue (boolean) — sitting in a queue awaiting attendant pickup isWaiting (boolean) — waiting on client response pendingResponse (boolean) ATTENDANTS (QUEUE OWNERSHIP) attendants[] (ObjectId[]) — current attendant user ids (the people who "own" the conversation right now) attendantsHistory[] (ObjectId[]) — append-only record of every attendant that ever touched the interaction CONTACT & SCOPE clientId (ObjectId) — reference to a CRM Person (contact) unitId (ObjectId), units[] (ObjectId[]) — org units the interaction is scoped to NESTED: MESSAGES (array) { createdAt, message (string, required), userId, clientId, isInternal, isHSM, attachmentId, attachments[], status: "read" | "delivered" | "sent" | "deleted" | "failed" | "warning" | "pending" | "spam" | "blocked", campaignId, templateId, emailTemplateId, context, quoteId, tokenUsage { input, output, cached }, cc, bcc } Server hooks: pushMessage() sets analytics.contactFirstMessageAt on first client message, scrapes OpenGraph metadata for referrals, and cascades tokenUsage. NESTED: TAGS (array) { tagId (ObjectId), type: "interaction" | "contact", assignedBy (ObjectId), assignedAt (Date) } NESTED: EVIDENCES (array) { addonId, message, entityId, itemId } — lightweight attachments / references from connected addons. NESTED: LOGS (array — append-only audit trail) { user, action, tagId, message, field, lastValue, newValue, updatedAt } NESTED: ANALYTICS { createdAt, liveAt, takeAt, answeredAt, finishedAt, csat (number), expiredBy (enum), expiredAt, contactFirstMessageAt } — durations between these fields drive most reports. CAMPAIGN / TEMPLATE TRACKING campaignId (ObjectId) — current top-level campaign campaigns[] — history: [{ campaignId, createdAt }] templates[] — history: [{ templateId, createdAt }] INDEXES (shape queries to hit these for heavy filters) (groupId, accountId, status) — always scoped + isLive, createdAt, clientId, activeChannelId/Key, isActive, units, "campaigns.campaignId", "evidences" Always include status:true (or rely on the default) and pivot on an indexed field to keep list queries cheap. RELATIONSHIPS Group 1 ── N Interaction.groupId Account 1 ── N Interaction.accountId Contact 1 ── N Interaction.clientId (CRM Person) User N ── M via attendants[] / attendantsHistory[] Campaign 1 ── N Interaction.campaignId; campaigns[] records the full campaign chain Tag N ── M via tags[].tagId (see smarttalks_tags) RESOURCES & ACTIONS interaction: list { query?, project?, options? } Mongo filter. Common shapes: { isActive: true, activeChannel: "whatsapp" } — open whatsapp chats { clientId: "<personId>" } — history for one contact { "campaigns.campaignId": "<campId>" } — interactions inside a campaign { "analytics.finishedAt": { $gte: "2026-01-01" } } — finished since a date { units: { $in: ["<unitId>"] } } — scoped to a set of units { attendants: "<userId>" } — assigned to a specific user options supports { limit, skip, sort }. Default limit is small — set explicitly for bulk reads. get { id } — full nested document (messages, tags, evidences, logs, analytics). update / delete — GATED OFF. Set SMART_MCP_ALLOW_HERMES_WRITE=true to enable. report: list { startDate?, endDate?, state?, type?, channel?, originType?, attendantId?, contactMotiveId?, campaignId?, limit?, cursor?, sortOrder? } Cursor-paginated aggregation over /v2/interactions/reports. All params optional, forwarded verbatim as querystring. cursor is an opaque token returned in a prior response. sortOrder: "asc" | "desc". Prefer this over interaction.list for large timeframes — results are cached (5min TTL) server-side. summary { ...same params } Cached top-line aggregation (counts / averages). Fast and lightweight; typical first call before drilling into report.list. audience: resolve { query, resolve?: boolean } POSTs /v1/interactions/audience with body { query } where query is a mongo blob against the audience criteria (usually pulled from a saved filter via smarttalks_filters). resolve=true materializes the audience (returns matching contact ids); resolve=false only previews the audience SIZE; omitting resolve skips the querystring altogether. Used by campaign designers to validate a filter before scheduling a campaign. campaign: status { id, payload?, query? } POSTs /v1/interactions/campaign/:id. Returns the optimized roll-up status for a single campaign (counts by phase, failure buckets, next-action hints). payload + query shape matches Hermes Interactions.campaignStatusOptimized. details { id, payload?, query?, project? } POSTs /v1/interactions/campaign/details/:id. Returns a per-interaction breakdown for a campaign; project lets you slice the document server-side. WRITES — GATED OFF interaction.update / interaction.delete are only registered when the server was started with SMART_MCP_ALLOW_HERMES_WRITE=true. Reads (interaction.list/get, report., audience.resolve, campaign.) are always available regardless of the gate. EXAMPLES interaction.list { query: { isActive: true, activeChannel: "whatsapp" }, options: { limit: 20, sort: { createdAt: -1 } } } interaction.get { id: "<interactionId>" } report.list { startDate: "2026-01-01", endDate: "2026-01-31", state: "finished", channel: "whatsapp", limit: 50, sortOrder: "desc" } report.summary { startDate: "2026-01-01", endDate: "2026-01-31" } audience.resolve { query: { tags: { $in: ["<tagId>"] } }, resolve: false } preview size (query usually from smarttalks_filters) audience.resolve { query: { tags: { $in: ["<tagId>"] } }, resolve: true } materialize campaign.status { id: "<campaignId>" } campaign.details { id: "<campaignId>", query: { "attendants.0": { $exists: true } } }

smarttalks_items

ChatGPT
Items — RECORDS that live inside a CRM Entity. An entity is a configurable schema (e.g. "Leads", "Deals", "Patients"); an item is one row/record of that schema. Every item is always scoped to one Entity AND the caller's tenant (groupId + accountId injected server-side from JWT). INPUT SHAPE { resource, action, entityId, ...args } entityId is REQUIRED on every call — items are always children of an entity. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented sequential identifier, human-friendly groupId (ObjectId, server-injected) — parent tenant Group accountId (ObjectId, server-injected) — parent Account entityId (ObjectId, required) — which Entity schema this item belongs to status (boolean, default true) — soft-delete; list queries exclude status:false by default createdAt, updatedAt (Date) — managed automatically createdBy, updatedBy (ObjectId — Users) CONTACT & UNIT SCOPING clientId (ObjectId) — optional link to a CRM Person / contact record units[] (ObjectId[]) — Unit ids this item is scoped to; injected from auth.units on create responsibleIds (string[]) — free-form responsible-user identifiers (default []) DYNAMIC FIELDS (entity-defined) fields[] — the core data of the item. Each element: { _id (ObjectId — entity field definition id), value (Mixed), updatedAt (Date), flags[] } flags[]: { userId (ObjectId), comment (string) } — per-user field-level annotations The set of valid _id values and their types come from the parent entity's layout. Field types of note: type 3 / 41 — date fields; pass ISO strings, server normalises to UTC start-of-day type 44 — appointment slot (capacity) type 46 — appointment status enum: "scheduled" | "rescheduled" | "confirmed" | "cancelled" | "no_show" | "show" type 53 — unique-constraint field; duplicate values are rejected (pass ignoreUnique:true to force merge) type 55 — kanban/status field; changes are appended to activities[] In list responses, fields are projected as a flat map keyed by fieldId string. ACTIVITIES (audit trail for status/kanban fields) activities[]: { fieldId (ObjectId), totalityFieldId (ObjectId), event ("change"), value (Mixed), changedAt (Date), changedBy (ObjectId) } Appended automatically by the server when a type-55 (kanban) field changes. INTERACTIONS interactions[] (ObjectId[]) — linked Hermes interaction ids APPOINTMENTS appointments[]: { scheduleId, appointmentId (ObjectId), ticket (string), status: "PENDING" | "CONFIRMED" | "CANCELLED" | "RESCHEDULED" | "REJECTED" | "SCHEDULED" | "NO_SHOW" | "SHOW", date (Date), startTime (Date), createdAt, updatedAt } NOTES notes[]: { _id, content (string, required), createdBy (ObjectId), createdAt (Date), updatedBy, updatedAt, deletedBy, deletedAt, interactionId (ObjectId) } Soft-deleted notes (deletedAt set) are filtered out by readNotes. Only the note's author or admin/manager/supervisor may edit or delete a note. EXIT REASON (funnel / stage-exit tracking) exitReason: { stage (string), reason (string|null), setAt (Date), setBy (ObjectId) } Managed via the PATCH /:entityId/items/:itemId/exit-reason route (not exposed in this tool — low-intent). MISC isMerged (boolean, default false) — set when two duplicate items were merged whatsAppBsuid (string) — WhatsApp Business Suite identifier INDEXES (shape queries to hit these) (groupId, accountId, units, entityId, createdAt) (groupId, accountId, clientId, status, units, entityId, _id) (groupId, accountId, "fields.value") (groupId, accountId, entityId, status, createdAt) Always scope on entityId + status:true; pivot on clientId or fields.$elemMatch for selectivity. RELATIONSHIPS Entity 1 ── N Item.entityId — every item belongs to one entity schema Contact 1 ── N Item.clientId — optional person link User 1 ── N Item.createdBy / updatedBy Unit N ── M via Item.units[] Interaction N ── M via Item.interactions[] RESOURCES & ACTIONS item: get { entityId, itemId } GET /v1/entities/:entityId/items/:itemId — full document with all nested subdocs. list { entityId, payload? } POST /v1/entities/:entityId/items — returns { items[], count, trash }. payload supports Mongo-style filters: { clientId: "<personId>" } — items for a contact { $and: [{ fields: { $elemMatch: { _id: "<fId>", value: "..." } } }] } — field filter { $and: [{ appointments: { $elemMatch: { date: "2026-04-27" } } }] } — by appointment date { flags: true } — items with flags from caller { notEmpty: true } — items with at least one field set payload also accepts: sort (Mongo sort object), skip, limit (default 10). create { entityId, payload } PUT /v1/entities/:entityId/items — payload must include fields[]. groupId/accountId/units server-injected. Server validates primary-field uniqueness unless auth.isApi. For entities of type 5 (appointment/schedule), pass date (type 41), time slot (type 42), capacity (type 44), status (type 46) fields. update { entityId, itemId, payload } PATCH /v1/entities/:entityId/items/:itemId — $set semantics. Include only fields you want to change. update_fields { entityId, itemId, fields[] } PATCH /v1/entities/:entityId/items/:itemId with body { fields[] } — bulk-replaces the provided fields while preserving all others (merge, not overwrite). Each element: { _id: "<fieldId>", value: any }. delete { entityId, itemId } DELETE /v1/entities/:entityId/items/:itemId — soft-delete (status:false). Triggers SDM event. field: get { entityId, itemId, fieldId } GET /v1/entities/:entityId/items/:itemId/fields/:fieldId — single field value with entity metadata. update { entityId, itemId, fieldId, payload } PATCH /v1/entities/:entityId/items/:itemId/fields/:fieldId payload must include value. Optional: ignoreUnique:true to force-overwrite unique fields. Date values (types 3/41) are normalised server-side; status fields (type 55) append an activity entry. note: list { entityId, itemId } GET /v1/entities/:entityId/items/:itemId/notes — active notes sorted newest-first. create { entityId, itemId, payload } POST /v1/entities/:entityId/items/:itemId/notes payload: { content (required), interactionId? } update { entityId, itemId, noteId, payload } PUT /v1/entities/:entityId/items/:itemId/notes/:noteId payload: { content (required) }. Only note author or admin/manager/supervisor may edit. delete { entityId, itemId, noteId } DELETE /v1/entities/:entityId/items/:itemId/notes/:noteId Soft-delete (sets deletedAt/deletedBy). Same author/role restriction as update. EXAMPLES item.list { entityId: "<eid>", payload: { clientId: "<personId>", limit: 20 } } item.list { entityId: "<eid>", payload: { $and: [{ fields: { $elemMatch: { _id: "<fId>", value: "hot" } } }], sort: { createdAt: -1 }, limit: 50 } } item.get { entityId: "<eid>", itemId: "<iid>" } item.create { entityId: "<eid>", payload: { fields: [{ _id: "<fId>", value: "Alice" }, { _id: "<f2Id>", value: "5511999999999" }] } } item.update { entityId: "<eid>", itemId: "<iid>", payload: { fields: [{ _id: "<fId>", value: "Bob" }] } } item.update_fields { entityId: "<eid>", itemId: "<iid>", fields: [{ _id: "<fId>", value: "new value" }] } item.delete { entityId: "<eid>", itemId: "<iid>" } field.get { entityId: "<eid>", itemId: "<iid>", fieldId: "<fId>" } field.update { entityId: "<eid>", itemId: "<iid>", fieldId: "<fId>", payload: { value: "updated" } } note.list { entityId: "<eid>", itemId: "<iid>" } note.create { entityId: "<eid>", itemId: "<iid>", payload: { content: "Follow-up scheduled" } } note.update { entityId: "<eid>", itemId: "<iid>", noteId: "<nid>", payload: { content: "Updated note" } } note.delete { entityId: "<eid>", itemId: "<iid>", noteId: "<nid>" } NOT EXPOSED (low-intent / specialised) PATCH /:entityId/items/:itemId/units — replace full units[] array (use item.update instead) PATCH /:entityId/items/:itemId/attach-unit — append single unit (internal routing helper) PATCH /:entityId/items/:itemId/exit-reason — set funnel exit stage/reason PUT /:entityId/items/:itemId/interactions — push a Hermes interaction id onto the item GET /:entityId/items/:itemId/formatted/:ex — formatted/display projection PUT /:entityId/items/:itemId/fields/:fieldId — update field by numeric type (not by id) POST /:entityId/options/items — select-box options aggregation

smarttalks_items_manage

ChatGPT
Items — RECORDS that live inside a CRM Entity. An entity is a configurable schema (e.g. "Leads", "Deals", "Patients"); an item is one row/record of that schema. Every item is always scoped to one Entity AND the caller's tenant (groupId + accountId injected server-side from JWT). INPUT SHAPE { resource, action, entityId, ...args } entityId is REQUIRED on every call — items are always children of an entity. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented sequential identifier, human-friendly groupId (ObjectId, server-injected) — parent tenant Group accountId (ObjectId, server-injected) — parent Account entityId (ObjectId, required) — which Entity schema this item belongs to status (boolean, default true) — soft-delete; list queries exclude status:false by default createdAt, updatedAt (Date) — managed automatically createdBy, updatedBy (ObjectId — Users) CONTACT & UNIT SCOPING clientId (ObjectId) — optional link to a CRM Person / contact record units[] (ObjectId[]) — Unit ids this item is scoped to; injected from auth.units on create responsibleIds (string[]) — free-form responsible-user identifiers (default []) DYNAMIC FIELDS (entity-defined) fields[] — the core data of the item. Each element: { _id (ObjectId — entity field definition id), value (Mixed), updatedAt (Date), flags[] } flags[]: { userId (ObjectId), comment (string) } — per-user field-level annotations The set of valid _id values and their types come from the parent entity's layout. Field types of note: type 3 / 41 — date fields; pass ISO strings, server normalises to UTC start-of-day type 44 — appointment slot (capacity) type 46 — appointment status enum: "scheduled" | "rescheduled" | "confirmed" | "cancelled" | "no_show" | "show" type 53 — unique-constraint field; duplicate values are rejected (pass ignoreUnique:true to force merge) type 55 — kanban/status field; changes are appended to activities[] In list responses, fields are projected as a flat map keyed by fieldId string. ACTIVITIES (audit trail for status/kanban fields) activities[]: { fieldId (ObjectId), totalityFieldId (ObjectId), event ("change"), value (Mixed), changedAt (Date), changedBy (ObjectId) } Appended automatically by the server when a type-55 (kanban) field changes. INTERACTIONS interactions[] (ObjectId[]) — linked Hermes interaction ids APPOINTMENTS appointments[]: { scheduleId, appointmentId (ObjectId), ticket (string), status: "PENDING" | "CONFIRMED" | "CANCELLED" | "RESCHEDULED" | "REJECTED" | "SCHEDULED" | "NO_SHOW" | "SHOW", date (Date), startTime (Date), createdAt, updatedAt } NOTES notes[]: { _id, content (string, required), createdBy (ObjectId), createdAt (Date), updatedBy, updatedAt, deletedBy, deletedAt, interactionId (ObjectId) } Soft-deleted notes (deletedAt set) are filtered out by readNotes. Only the note's author or admin/manager/supervisor may edit or delete a note. EXIT REASON (funnel / stage-exit tracking) exitReason: { stage (string), reason (string|null), setAt (Date), setBy (ObjectId) } Managed via the PATCH /:entityId/items/:itemId/exit-reason route (not exposed in this tool — low-intent). MISC isMerged (boolean, default false) — set when two duplicate items were merged whatsAppBsuid (string) — WhatsApp Business Suite identifier INDEXES (shape queries to hit these) (groupId, accountId, units, entityId, createdAt) (groupId, accountId, clientId, status, units, entityId, _id) (groupId, accountId, "fields.value") (groupId, accountId, entityId, status, createdAt) Always scope on entityId + status:true; pivot on clientId or fields.$elemMatch for selectivity. RELATIONSHIPS Entity 1 ── N Item.entityId — every item belongs to one entity schema Contact 1 ── N Item.clientId — optional person link User 1 ── N Item.createdBy / updatedBy Unit N ── M via Item.units[] Interaction N ── M via Item.interactions[] RESOURCES & ACTIONS item: get { entityId, itemId } GET /v1/entities/:entityId/items/:itemId — full document with all nested subdocs. list { entityId, payload? } POST /v1/entities/:entityId/items — returns { items[], count, trash }. payload supports Mongo-style filters: { clientId: "<personId>" } — items for a contact { $and: [{ fields: { $elemMatch: { _id: "<fId>", value: "..." } } }] } — field filter { $and: [{ appointments: { $elemMatch: { date: "2026-04-27" } } }] } — by appointment date { flags: true } — items with flags from caller { notEmpty: true } — items with at least one field set payload also accepts: sort (Mongo sort object), skip, limit (default 10). create { entityId, payload } PUT /v1/entities/:entityId/items — payload must include fields[]. groupId/accountId/units server-injected. Server validates primary-field uniqueness unless auth.isApi. For entities of type 5 (appointment/schedule), pass date (type 41), time slot (type 42), capacity (type 44), status (type 46) fields. update { entityId, itemId, payload } PATCH /v1/entities/:entityId/items/:itemId — $set semantics. Include only fields you want to change. update_fields { entityId, itemId, fields[] } PATCH /v1/entities/:entityId/items/:itemId with body { fields[] } — bulk-replaces the provided fields while preserving all others (merge, not overwrite). Each element: { _id: "<fieldId>", value: any }. delete { entityId, itemId } DELETE /v1/entities/:entityId/items/:itemId — soft-delete (status:false). Triggers SDM event. field: get { entityId, itemId, fieldId } GET /v1/entities/:entityId/items/:itemId/fields/:fieldId — single field value with entity metadata. update { entityId, itemId, fieldId, payload } PATCH /v1/entities/:entityId/items/:itemId/fields/:fieldId payload must include value. Optional: ignoreUnique:true to force-overwrite unique fields. Date values (types 3/41) are normalised server-side; status fields (type 55) append an activity entry. note: list { entityId, itemId } GET /v1/entities/:entityId/items/:itemId/notes — active notes sorted newest-first. create { entityId, itemId, payload } POST /v1/entities/:entityId/items/:itemId/notes payload: { content (required), interactionId? } update { entityId, itemId, noteId, payload } PUT /v1/entities/:entityId/items/:itemId/notes/:noteId payload: { content (required) }. Only note author or admin/manager/supervisor may edit. delete { entityId, itemId, noteId } DELETE /v1/entities/:entityId/items/:itemId/notes/:noteId Soft-delete (sets deletedAt/deletedBy). Same author/role restriction as update. EXAMPLES item.list { entityId: "<eid>", payload: { clientId: "<personId>", limit: 20 } } item.list { entityId: "<eid>", payload: { $and: [{ fields: { $elemMatch: { _id: "<fId>", value: "hot" } } }], sort: { createdAt: -1 }, limit: 50 } } item.get { entityId: "<eid>", itemId: "<iid>" } item.create { entityId: "<eid>", payload: { fields: [{ _id: "<fId>", value: "Alice" }, { _id: "<f2Id>", value: "5511999999999" }] } } item.update { entityId: "<eid>", itemId: "<iid>", payload: { fields: [{ _id: "<fId>", value: "Bob" }] } } item.update_fields { entityId: "<eid>", itemId: "<iid>", fields: [{ _id: "<fId>", value: "new value" }] } item.delete { entityId: "<eid>", itemId: "<iid>" } field.get { entityId: "<eid>", itemId: "<iid>", fieldId: "<fId>" } field.update { entityId: "<eid>", itemId: "<iid>", fieldId: "<fId>", payload: { value: "updated" } } note.list { entityId: "<eid>", itemId: "<iid>" } note.create { entityId: "<eid>", itemId: "<iid>", payload: { content: "Follow-up scheduled" } } note.update { entityId: "<eid>", itemId: "<iid>", noteId: "<nid>", payload: { content: "Updated note" } } note.delete { entityId: "<eid>", itemId: "<iid>", noteId: "<nid>" } NOT EXPOSED (low-intent / specialised) PATCH /:entityId/items/:itemId/units — replace full units[] array (use item.update instead) PATCH /:entityId/items/:itemId/attach-unit — append single unit (internal routing helper) PATCH /:entityId/items/:itemId/exit-reason — set funnel exit stage/reason PUT /:entityId/items/:itemId/interactions — push a Hermes interaction id onto the item GET /:entityId/items/:itemId/formatted/:ex — formatted/display projection PUT /:entityId/items/:itemId/fields/:fieldId — update field by numeric type (not by id) POST /:entityId/options/items — select-box options aggregation

smarttalks_payment_intent

ChatGPT
Payment intents — PAYMENT LINK & INTENT MANAGEMENT. A payment intent represents a single charge request against a CRM contact, tracked from creation through completion. Scoped to one Group + Account; every read/write is auto-scoped to the caller's JWT claims. INPUT SHAPE { action, ...args } IDENTITY & TENANCY (applies to every record) _id (ObjectId) — primary key; also forms the payment URL: https://pay.smarttalks.ai/<_id> groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account contactId (ObjectId, required) — links to a CRM Item (contact person) createdAt, updatedAt (Date) — managed automatically MONEY FIELDS amount (number, required, computed) — base charge in the chosen currency; derived server-side as sum(items[].quantity items[].unitPrice). Do NOT send this on create. grossAmount (number, required, computed) — effective charge after discounts; starts equal to amount. discount (number) — absolute discount amount in the same currency; written by applyDiscount. voucherDiscount (string) — promo/voucher code string applied at create time. voucherDiscountAmount (number) — resolved voucher value. voucherDiscountType (enum: "percent" | "fixed") — how the voucher was applied. currency (enum: "BRL" | "USD" | "EUR" | "ARC" | "UYU" | "PYG", required, default "BRL") installments (number, default null, min 0) — number of credit-card installments; null means single charge. STATUS LIFECYCLE status (enum, default "pending"): "pending" — created, awaiting payer action "analysis" — payment submitted, gateway is verifying "completed" — payment confirmed and settled "completed_no_kanban" — payment confirmed but kanban pipeline step skipped "failed" — payment attempt failed; may be retried up to retryCount ceiling "cancelled" — explicitly cancelled; effectively terminal (no built-in recovery) "expired" — link expired (expiresAt passed or expiration job ran) "integration_fail" — downstream integration error (e.g. hotel system) retryCount (number, default 0) — how many times payment has been re-attempted. paidAt (Date) — when payer completed payment. confirmedAt (Date) — when the gateway confirmed the funds. expiredAt (Date) — when the record was marked expired. expiresAt (Date) — requested expiry; an expiration job runs at this time. PAYMENT METHOD method (enum, required, default "link"): "link" — hosted payment page (payer chooses method); most common "credit_card" — direct card charge "debit_card" — direct debit card "bank_transfer" — wire transfer "cash" — cash payment recorded manually "apple_pay" — Apple Pay "google_pay" — Google Pay "pix" — Brazilian instant payment (PIX) paymentNsu (string) — payment network sequence number (written by gateway webhook). paymentReference (string) — gateway transaction reference. paymentAuthCode (string) — gateway authorization code. billCode (string) — boleto/bill barcode (bank_transfer / boleto flows). billUrl (string) — URL to download/view the boleto. billAccessKey (string) — NF-e access key for fiscal documents. LINE ITEMS (array, required on create — at least one) items[]: reference (string, required) — SKU / external product code description (string, required) — human-readable label shown on the hosted page quantity (number, required) unitPrice (number, required) — price per unit; amount = sum(quantity unitPrice) customData (object, default {}) — arbitrary metadata (booking ids, seat numbers, etc.) ENVIRONMENT env (enum: "prod" | "hom", default "prod") — target payment gateway environment. Use "hom" ONLY in test/staging contexts; "hom" payments route to sandbox gateways. REFERENCES & LOCATORS reference (string) — caller's external reference (e.g. booking number); used by discount rules Note: the "pix-hotel" discount rule checks reference.startsWith("H-") locator (string) — reservation locator code hotelCode (string) — property code for hotel integrations RESCHEDULE FLOW reschedule (boolean, default false) — marks this payment as part of a reschedule operation rescheduleData (object) — required when reschedule: true bill (number, required) — original bill number being rescheduled products[] (array, required, non-empty): { path, amount, date, schedule, children[] } person (object, required): { name, cpf (required), email (required), cellPhone (required), homePhone } REVALIDATION revalidated (boolean, default false) — flag set by gateway when a stale intent is re-validated lastReservationAt (Date) — last time a reservation was attempted against this intent DISCOUNT RULES (applyDiscount / revertDiscount) Rules are server-defined named functions. Currently available: "pix-hotel" — 5% off grossAmount; only applies when reference starts with "H-" applyDiscount: writes discount and grossAmount; status must be "pending". revertDiscount: unsets discount and grossAmount; idempotent (no-op if no discount applied); status must be "pending". Both endpoints require the internal API key (Utils.isApi middleware) — they are NOT callable by regular user JWTs. Contact the platform team if you need to trigger discounts from MCP. RELATIONSHIPS Group 1 ── N PaymentIntent.groupId Account 1 ── N PaymentIntent.accountId CRM Item 1 ── N PaymentIntent.contactId (the paying contact) ACTIONS list { contactId?, status?, method?, sortBy?, sortOrder?, skip?, limit? } Returns { items[], count }. Defaults: limit=10, skip=0, sortBy=createdAt, sortOrder=desc. status filter is limited to: "pending" | "completed" | "failed" | "cancelled" | "expired" (the API's list endpoint does not expose "analysis", "integration_fail", "completed_no_kanban"). get { id } Full document including all nested/computed fields. create { items, contactId, currency?, method?, description?, reference?, locator?, expiresAt?, voucherDiscount?, env?, hotelCode?, reschedule?, rescheduleData? } Returns { result, urlMakePayment } where urlMakePayment = "https://pay.smarttalks.ai/<id>". Minimum viable payload: { items: [{ reference, description, quantity, unitPrice }], contactId } updateStatus { id, status } CAUTION: "completed" and "cancelled" are effectively irreversible terminal states. Verify the current status before driving to either of these. applyDiscount { id, rule } CAUTION: Modifies money fields (grossAmount). Only applies when status === "pending". Returns { _id, amount, discount, grossAmount, pixDiscountPercent, rule }. REQUIRES internal API key — not callable from user JWTs in standard deployments. revertDiscount { id, rule } CAUTION: Modifies money fields (grossAmount). Only applies when status === "pending". Idempotent — safe to call even if no discount is currently applied. REQUIRES internal API key — not callable from user JWTs in standard deployments. biExport { startDate?, endDate?, status?, method?, currency?, contactId?, groupBy?, sortOrder?, includeDetails?, limit?, page? } Aggregation + optional raw-record export for BI dashboards. Returns { summary, aggregated[], details?, metadata }. groupBy: "day" | "week" | "month" | "status" | "method" | "currency" (default: "day"). EXAMPLES list { action: "list", status: "pending", limit: 20 } get { action: "get", id: "<intentId>" } create { action: "create", contactId: "<contactId>", currency: "BRL", method: "pix", items: [{ reference: "PROD-1", description: "Hotel night", quantity: 2, unitPrice: 350.00 }] } updateStatus { action: "updateStatus", id: "<intentId>", status: "cancelled" } applyDiscount { action: "applyDiscount", id: "<intentId>", rule: "pix-hotel" } revertDiscount { action: "revertDiscount", id: "<intentId>", rule: "pix-hotel" } biExport { action: "biExport", startDate: "2026-01-01T00:00:00Z", endDate: "2026-01-31T23:59:59Z", groupBy: "method", includeDetails: false }

smarttalks_payment_intent_manage

ChatGPT
Payment intents — PAYMENT LINK & INTENT MANAGEMENT. A payment intent represents a single charge request against a CRM contact, tracked from creation through completion. Scoped to one Group + Account; every read/write is auto-scoped to the caller's JWT claims. INPUT SHAPE { action, ...args } IDENTITY & TENANCY (applies to every record) _id (ObjectId) — primary key; also forms the payment URL: https://pay.smarttalks.ai/<_id> groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account contactId (ObjectId, required) — links to a CRM Item (contact person) createdAt, updatedAt (Date) — managed automatically MONEY FIELDS amount (number, required, computed) — base charge in the chosen currency; derived server-side as sum(items[].quantity items[].unitPrice). Do NOT send this on create. grossAmount (number, required, computed) — effective charge after discounts; starts equal to amount. discount (number) — absolute discount amount in the same currency; written by applyDiscount. voucherDiscount (string) — promo/voucher code string applied at create time. voucherDiscountAmount (number) — resolved voucher value. voucherDiscountType (enum: "percent" | "fixed") — how the voucher was applied. currency (enum: "BRL" | "USD" | "EUR" | "ARC" | "UYU" | "PYG", required, default "BRL") installments (number, default null, min 0) — number of credit-card installments; null means single charge. STATUS LIFECYCLE status (enum, default "pending"): "pending" — created, awaiting payer action "analysis" — payment submitted, gateway is verifying "completed" — payment confirmed and settled "completed_no_kanban" — payment confirmed but kanban pipeline step skipped "failed" — payment attempt failed; may be retried up to retryCount ceiling "cancelled" — explicitly cancelled; effectively terminal (no built-in recovery) "expired" — link expired (expiresAt passed or expiration job ran) "integration_fail" — downstream integration error (e.g. hotel system) retryCount (number, default 0) — how many times payment has been re-attempted. paidAt (Date) — when payer completed payment. confirmedAt (Date) — when the gateway confirmed the funds. expiredAt (Date) — when the record was marked expired. expiresAt (Date) — requested expiry; an expiration job runs at this time. PAYMENT METHOD method (enum, required, default "link"): "link" — hosted payment page (payer chooses method); most common "credit_card" — direct card charge "debit_card" — direct debit card "bank_transfer" — wire transfer "cash" — cash payment recorded manually "apple_pay" — Apple Pay "google_pay" — Google Pay "pix" — Brazilian instant payment (PIX) paymentNsu (string) — payment network sequence number (written by gateway webhook). paymentReference (string) — gateway transaction reference. paymentAuthCode (string) — gateway authorization code. billCode (string) — boleto/bill barcode (bank_transfer / boleto flows). billUrl (string) — URL to download/view the boleto. billAccessKey (string) — NF-e access key for fiscal documents. LINE ITEMS (array, required on create — at least one) items[]: reference (string, required) — SKU / external product code description (string, required) — human-readable label shown on the hosted page quantity (number, required) unitPrice (number, required) — price per unit; amount = sum(quantity unitPrice) customData (object, default {}) — arbitrary metadata (booking ids, seat numbers, etc.) ENVIRONMENT env (enum: "prod" | "hom", default "prod") — target payment gateway environment. Use "hom" ONLY in test/staging contexts; "hom" payments route to sandbox gateways. REFERENCES & LOCATORS reference (string) — caller's external reference (e.g. booking number); used by discount rules Note: the "pix-hotel" discount rule checks reference.startsWith("H-") locator (string) — reservation locator code hotelCode (string) — property code for hotel integrations RESCHEDULE FLOW reschedule (boolean, default false) — marks this payment as part of a reschedule operation rescheduleData (object) — required when reschedule: true bill (number, required) — original bill number being rescheduled products[] (array, required, non-empty): { path, amount, date, schedule, children[] } person (object, required): { name, cpf (required), email (required), cellPhone (required), homePhone } REVALIDATION revalidated (boolean, default false) — flag set by gateway when a stale intent is re-validated lastReservationAt (Date) — last time a reservation was attempted against this intent DISCOUNT RULES (applyDiscount / revertDiscount) Rules are server-defined named functions. Currently available: "pix-hotel" — 5% off grossAmount; only applies when reference starts with "H-" applyDiscount: writes discount and grossAmount; status must be "pending". revertDiscount: unsets discount and grossAmount; idempotent (no-op if no discount applied); status must be "pending". Both endpoints require the internal API key (Utils.isApi middleware) — they are NOT callable by regular user JWTs. Contact the platform team if you need to trigger discounts from MCP. RELATIONSHIPS Group 1 ── N PaymentIntent.groupId Account 1 ── N PaymentIntent.accountId CRM Item 1 ── N PaymentIntent.contactId (the paying contact) ACTIONS list { contactId?, status?, method?, sortBy?, sortOrder?, skip?, limit? } Returns { items[], count }. Defaults: limit=10, skip=0, sortBy=createdAt, sortOrder=desc. status filter is limited to: "pending" | "completed" | "failed" | "cancelled" | "expired" (the API's list endpoint does not expose "analysis", "integration_fail", "completed_no_kanban"). get { id } Full document including all nested/computed fields. create { items, contactId, currency?, method?, description?, reference?, locator?, expiresAt?, voucherDiscount?, env?, hotelCode?, reschedule?, rescheduleData? } Returns { result, urlMakePayment } where urlMakePayment = "https://pay.smarttalks.ai/<id>". Minimum viable payload: { items: [{ reference, description, quantity, unitPrice }], contactId } updateStatus { id, status } CAUTION: "completed" and "cancelled" are effectively irreversible terminal states. Verify the current status before driving to either of these. applyDiscount { id, rule } CAUTION: Modifies money fields (grossAmount). Only applies when status === "pending". Returns { _id, amount, discount, grossAmount, pixDiscountPercent, rule }. REQUIRES internal API key — not callable from user JWTs in standard deployments. revertDiscount { id, rule } CAUTION: Modifies money fields (grossAmount). Only applies when status === "pending". Idempotent — safe to call even if no discount is currently applied. REQUIRES internal API key — not callable from user JWTs in standard deployments. biExport { startDate?, endDate?, status?, method?, currency?, contactId?, groupBy?, sortOrder?, includeDetails?, limit?, page? } Aggregation + optional raw-record export for BI dashboards. Returns { summary, aggregated[], details?, metadata }. groupBy: "day" | "week" | "month" | "status" | "method" | "currency" (default: "day"). EXAMPLES list { action: "list", status: "pending", limit: 20 } get { action: "get", id: "<intentId>" } create { action: "create", contactId: "<contactId>", currency: "BRL", method: "pix", items: [{ reference: "PROD-1", description: "Hotel night", quantity: 2, unitPrice: 350.00 }] } updateStatus { action: "updateStatus", id: "<intentId>", status: "cancelled" } applyDiscount { action: "applyDiscount", id: "<intentId>", rule: "pix-hotel" } revertDiscount { action: "revertDiscount", id: "<intentId>", rule: "pix-hotel" } biExport { action: "biExport", startDate: "2026-01-01T00:00:00Z", endDate: "2026-01-31T23:59:59Z", groupBy: "method", includeDetails: false }

smarttalks_permissions_groups

ChatGPT
Permission groups, REUSABLE ROLE BUNDLES. A permission group names a set of route-level, module-level, and capability permissions that can be attached to users and tokens. One Account owns many groups; users and tokens reference one or more groups. IDENTITY _id (ObjectId) — document id label (string, required, trimmed) — human-readable display name for the group (e.g. "Supervisor", "Read-only Agent") accountId (ObjectId, required) — the Account that owns this group; server injects from auth context on create PERMISSIONS MATRIX routes[] — array of API-route permission entries: routes[].path (string) — URL path pattern, supports trailing wildcards (e.g. "/v1/users", "/v1/") Special rule: path "/v1/" grants "write" to ALL routes, bypassing per-route checks. Matching uses a wildcard-to-regex converter; first match wins. routes[].permission (string: "read" | "write" | "none") — access level for that path "write" — allows ALL HTTP methods (GET, POST, PUT, PATCH, DELETE, OPTIONS) "read" — allows GET, OPTIONS, and body-less POST "none" — denies all methods; equivalent to omitting the route entry modules[] — array of product-module permission entries (UI feature gating): modules[].name (string) — module identifier (e.g. "admin", "schedules", "appointments", "reports", …) modules[].permission (string: "read" | "write" | "none") — access level within that module entities[] (ObjectId[]) — reserved array for entity-scoped permission overrides (rarely populated) CAPABILITIES (can flags — all boolean) canSeeAllQueues (default false) — user can view interactions across ALL queues, not just those they are assigned to canSeeAllInteractions (default false) — user can see every interaction in the account, regardless of queue membership canAssign (default false) — user can assign an open interaction to themselves or another agent canTransfer (default false) — user can transfer an active interaction to a different queue or agent canFinish (default false) — user can close/finish an interaction (mark it as done) canHsm (default false) — user can send HSM (Highly Structured Message / WhatsApp template) messages to contacts canSendAudio (default true) — user can record and send audio/voice messages within an interaction canRemoveContact (default false) — user can permanently remove a contact from the system canExportInteractions (default false) — user can export interaction data (e.g. CSV download) canManageInteractionTags (default false) — user can create, edit, and delete tags on interactions canFilterByResponsible (default false) — user can filter the interaction list by the responsible/assigned agent TASK PERMISSIONS (govern the smarttalks_tasks tool's task subsystem) Convention: baseline self-scoped abilities default true; elevated or cross-user actions default false. Each canScope is an enum ("own" | "department" | "unit" | "account") setting how wide the paired ability reaches. canCreateTaskForSelf (default true) — create tasks assigned to oneself canCreateTaskForOthers (default false) — create tasks assigned to other users canCreateTaskForOthersScope (default "department") — reach of canCreateTaskForOthers canViewTasks (default true) — read tasks canViewTasksScope (default "own") — reach of canViewTasks canViewMetrics (default false) — view task metrics/analytics canViewMetricsScope (default "own") — reach of canViewMetrics canViewActivityLog (default false) — view a task's activity log canViewArchivedTasks (default false) — view archived tasks canViewArchivedTasksScope (default "own") — reach of canViewArchivedTasks canEditTasks (default true) — edit task fields canEditTasksScope (default "own") — reach of canEditTasks canChangeDueDate (default true) — change a task's due date canChangeDueDateScope (default "own") — reach of canChangeDueDate canReassignTasks (default false) — reassign a task to a different user canReassignTasksScope (default "department") — reach of canReassignTasks canManageStatus (default true) — transition task status (complete, cancel, reopen, …) canManageStatusScope (default "own") — reach of canManageStatus canDeleteTasks (default false) — soft-delete tasks canDeleteTasksScope (default "own") — reach of canDeleteTasks canExportArchived (default false) — export archived tasks AGENT BUILDER PERMISSIONS canAnnotateAgentBuilder (default false) — write observability annotations on Agent Builder traces/runs. Feedback thumbs stay open to all account users canManageAgentBuilderEvals (default false) — dataset CRUD, eval-run triggers, and grader editing in the Managed Evals tab. Grader viewing stays open canPublishAgentBuilder (default false) — publish a new live Agent Builder package version. Authoring drafts stays open to any tenant LIFECYCLE status (boolean, default true) — soft-delete flag; list queries automatically exclude status:false documents isSystem (boolean, default false) — WARNING: when true this group is managed by the platform itself. DO NOT modify or delete isSystem:true groups — doing so will break built-in platform roles. createdAt (Date, default now), updatedAt (Date, auto-updated on every write) createdBy (ObjectId — User), updatedBy (ObjectId — User), deletedBy (ObjectId — User) RELATIONSHIPS smarttalks_accounts 1 ── N PermissionsGroup.accountId (smarttalks_accounts is the owner) PermissionsGroup N ── M smarttalks_users via User.permissionsGroups[] (users reference this group) PermissionsGroup N ── M smarttalks_tokens via Token.permissionsGroups[] (tokens reference this group) ACTIONS get { id } — fetch one permission group by id (cached for ~120 s) list { query, project?, options? } — Mongo filter scoped to caller's accountId. Common: {} — all active groups for the account { isSystem: true } — built-in system groups only { label: /supervisor/i } — fuzzy label search { canAssign: true, canTransfer: true } — groups that have assign+transfer capabilities Default list projection returns counts of routes/modules rather than their full arrays. options supports { limit (default 10), skip (default 0) } create { payload } — required: label; accountId is server-injected from auth context. optional: routes[], modules[], entities[], any can* flag. Never set isSystem:true manually. update { id, payload } — mutate any mutable field. NEVER update a group where isSystem:true. delete { id } — soft-delete (sets status:false, records deletedBy)

smarttalks_permissions_groups_manage

ChatGPT
Permission groups, REUSABLE ROLE BUNDLES. A permission group names a set of route-level, module-level, and capability permissions that can be attached to users and tokens. One Account owns many groups; users and tokens reference one or more groups. IDENTITY _id (ObjectId) — document id label (string, required, trimmed) — human-readable display name for the group (e.g. "Supervisor", "Read-only Agent") accountId (ObjectId, required) — the Account that owns this group; server injects from auth context on create PERMISSIONS MATRIX routes[] — array of API-route permission entries: routes[].path (string) — URL path pattern, supports trailing wildcards (e.g. "/v1/users", "/v1/") Special rule: path "/v1/" grants "write" to ALL routes, bypassing per-route checks. Matching uses a wildcard-to-regex converter; first match wins. routes[].permission (string: "read" | "write" | "none") — access level for that path "write" — allows ALL HTTP methods (GET, POST, PUT, PATCH, DELETE, OPTIONS) "read" — allows GET, OPTIONS, and body-less POST "none" — denies all methods; equivalent to omitting the route entry modules[] — array of product-module permission entries (UI feature gating): modules[].name (string) — module identifier (e.g. "admin", "schedules", "appointments", "reports", …) modules[].permission (string: "read" | "write" | "none") — access level within that module entities[] (ObjectId[]) — reserved array for entity-scoped permission overrides (rarely populated) CAPABILITIES (can flags — all boolean) canSeeAllQueues (default false) — user can view interactions across ALL queues, not just those they are assigned to canSeeAllInteractions (default false) — user can see every interaction in the account, regardless of queue membership canAssign (default false) — user can assign an open interaction to themselves or another agent canTransfer (default false) — user can transfer an active interaction to a different queue or agent canFinish (default false) — user can close/finish an interaction (mark it as done) canHsm (default false) — user can send HSM (Highly Structured Message / WhatsApp template) messages to contacts canSendAudio (default true) — user can record and send audio/voice messages within an interaction canRemoveContact (default false) — user can permanently remove a contact from the system canExportInteractions (default false) — user can export interaction data (e.g. CSV download) canManageInteractionTags (default false) — user can create, edit, and delete tags on interactions canFilterByResponsible (default false) — user can filter the interaction list by the responsible/assigned agent TASK PERMISSIONS (govern the smarttalks_tasks tool's task subsystem) Convention: baseline self-scoped abilities default true; elevated or cross-user actions default false. Each canScope is an enum ("own" | "department" | "unit" | "account") setting how wide the paired ability reaches. canCreateTaskForSelf (default true) — create tasks assigned to oneself canCreateTaskForOthers (default false) — create tasks assigned to other users canCreateTaskForOthersScope (default "department") — reach of canCreateTaskForOthers canViewTasks (default true) — read tasks canViewTasksScope (default "own") — reach of canViewTasks canViewMetrics (default false) — view task metrics/analytics canViewMetricsScope (default "own") — reach of canViewMetrics canViewActivityLog (default false) — view a task's activity log canViewArchivedTasks (default false) — view archived tasks canViewArchivedTasksScope (default "own") — reach of canViewArchivedTasks canEditTasks (default true) — edit task fields canEditTasksScope (default "own") — reach of canEditTasks canChangeDueDate (default true) — change a task's due date canChangeDueDateScope (default "own") — reach of canChangeDueDate canReassignTasks (default false) — reassign a task to a different user canReassignTasksScope (default "department") — reach of canReassignTasks canManageStatus (default true) — transition task status (complete, cancel, reopen, …) canManageStatusScope (default "own") — reach of canManageStatus canDeleteTasks (default false) — soft-delete tasks canDeleteTasksScope (default "own") — reach of canDeleteTasks canExportArchived (default false) — export archived tasks AGENT BUILDER PERMISSIONS canAnnotateAgentBuilder (default false) — write observability annotations on Agent Builder traces/runs. Feedback thumbs stay open to all account users canManageAgentBuilderEvals (default false) — dataset CRUD, eval-run triggers, and grader editing in the Managed Evals tab. Grader viewing stays open canPublishAgentBuilder (default false) — publish a new live Agent Builder package version. Authoring drafts stays open to any tenant LIFECYCLE status (boolean, default true) — soft-delete flag; list queries automatically exclude status:false documents isSystem (boolean, default false) — WARNING: when true this group is managed by the platform itself. DO NOT modify or delete isSystem:true groups — doing so will break built-in platform roles. createdAt (Date, default now), updatedAt (Date, auto-updated on every write) createdBy (ObjectId — User), updatedBy (ObjectId — User), deletedBy (ObjectId — User) RELATIONSHIPS smarttalks_accounts 1 ── N PermissionsGroup.accountId (smarttalks_accounts is the owner) PermissionsGroup N ── M smarttalks_users via User.permissionsGroups[] (users reference this group) PermissionsGroup N ── M smarttalks_tokens via Token.permissionsGroups[] (tokens reference this group) ACTIONS get { id } — fetch one permission group by id (cached for ~120 s) list { query, project?, options? } — Mongo filter scoped to caller's accountId. Common: {} — all active groups for the account { isSystem: true } — built-in system groups only { label: /supervisor/i } — fuzzy label search { canAssign: true, canTransfer: true } — groups that have assign+transfer capabilities Default list projection returns counts of routes/modules rather than their full arrays. options supports { limit (default 10), skip (default 0) } create { payload } — required: label; accountId is server-injected from auth context. optional: routes[], modules[], entities[], any can* flag. Never set isSystem:true manually. update { id, payload } — mutate any mutable field. NEVER update a group where isSystem:true. delete { id } — soft-delete (sets status:false, records deletedBy)

smarttalks_queues

ChatGPT
Queues — INTERACTION ROUTING QUEUES. A queue belongs to ONE Group and ONE Account and fans out to MANY Departments (via departments[]). Queues drive how live interactions are distributed to users. FIELDS _id (ObjectId), label (string, required) — display name caption (string) — short description groupId (ObjectId, required, server-injected), accountId (ObjectId, required, server-injected) isActive (boolean, default true) — whether the queue actively receives new interactions status (boolean, default true) — soft-delete departments[] (ObjectId[]) — Departments that staff this queue (see smarttalks_departments); users in those departments are the candidate pool for routing roles (object) — routing behaviour: roles.isCheckIn (boolean, default true) — require user check-in roles.isForcePass (boolean, default false) — see DISTRIBUTION STRATEGIES below (controls whether offline/away users are still candidates) roles.isQueue (boolean, default true) — treat as a queue (vs direct-attend) roles.positionTransmission (number, default 0) — how often the queue position is broadcast to the client (seconds) roles.maximumPerAttendant (number, default 10) — per-user concurrent-interaction cap for THIS queue (used as the capacity ceiling when a user has no personal maxConcurrentCalls override) roles.distribution (enum: "balance" | "sequential" | "open", default "open") — routing strategy; see DISTRIBUTION STRATEGIES below for the behavioural differences roles.disableAudio (boolean), roles.disableAnnex (boolean), roles.disableEmojis (boolean) roles.automations[] (ObjectId[]) — automation ids expiration (object) — timeout behaviour, three independent sub-policies: expiration.user { isActive, time (s), finishWithoutCsat, message } — idle user timeout expiration.client { isActive, time (s), finishWithoutCsat, message } — idle client timeout expiration.queue { isActive, time (min 5), redirectType: "FLOW"|"UNIT"|"QUEUE"|"ALERT_NOTIFICATION", flowId (ObjectId), blockId (ObjectId), queueId (ObjectId), unitId (ObjectId), units[] (ObjectId[]), tags[] (ObjectId[]), message, notificationMessage } — what to do when an interaction ages out in the queue createdBy, updatedBy (ObjectId — Users), createdAt, updatedAt DISTRIBUTION STRATEGIES — how roles.distribution affects routing All three strategies share a CANDIDATE POOL built by FindUsersByDepartmentService: - Users whose department ∈ queue.departments[] - User.receiveAutomatedAttendances === true (for automated strategies; not required for "open") - If the interaction carries units[], users are further restricted to those who belong to at least one of those units - The caller (auth._id) is excluded from the candidate pool so an agent can't self-assign They also share a CAPACITY CHECK per candidate, priority-ordered: 1. If User.unlimitedConcurrentCalls === true → no cap, always eligible 2. Else if User.maxConcurrentCalls != null → compare to the user's TOTAL active interactions across ALL queues (0 means "cannot receive") 3. Else → compare the user's count in THIS queue only against roles.maximumPerAttendant If no candidate has capacity, the interaction is left in the queue and an expiration job is scheduled via ExpirationService.setQueueJob (which eventually triggers the expiration.queue policy above). • "open" (default) — PASSIVE / BROADCAST No server-side assignment. A socket toast "New client waiting for attendance" is emitted to every user in the candidate pool. The first user to self-pick claims the interaction (manual pull). Best for: small teams, homogeneous skill pool, or when you want agents to choose. Note: roles.isForcePass has no effect on "open" — since no automated filter runs, away/offline users simply don't see the toast. • "balance" — LEAST-LOADED Automatically assigns the interaction to the candidate with the FEWEST active interactions. Sort order (primary → tertiary): 1. attendingTotal across all queues (ascending — lowest workload wins) 2. availability priority: online (1) > away (2) > offline (3) 3. onlineAt ascending (longest currently-online wins the tie) Eligibility default: only availability === "online" users are considered. If roles.isForcePass === true: away/offline users become eligible too (so the queue never stalls when no one is online). Best for: uneven agent productivity, minimize customer wait time. • "sequential" — ROUND-ROBIN Automatically assigns the interaction to the NEXT user in rotation. Rotation state is stored in Redis at the key account:<accountId>:queue:<queueId>[:units:<unit-ids-joined-by-dash>]:position so each (account, queue, units-slice) tuple has its own independent rotation counter. Selection: 1. Read cached position, pick position+1 (wrap to 0 at end of array) 2. If that candidate is at capacity, scan FORWARD for the next one with capacity; if none found, scan from the beginning 3. Write the final position back to cache Within the candidate list, users are sorted by availability then onlineAt (same secondary/tertiary rules as balance) for deterministic ordering. Eligibility default: only availability === "online" users are considered. If roles.isForcePass === true: away/offline users become eligible too. Best for: homogeneous skill pool where fairness ("nobody skipped") matters more than load-optimality. COMPARISON AT A GLANCE | Strategy | Who picks? | Selection basis | State | Fairness | Typical use | | ---------- | ---------- | --------------------- | ------------ | ------------ | ----------- | | open | the user | broadcast to all | none | self-service | small teams, agent choice | | balance | the server | lowest workload | in-request | load-based | uneven productivity, minimize wait | | sequential | the server | round-robin position | Redis cache | position-based | equal skill, strict rotation | RELATIONSHIPS Group 1 ── N Queue.groupId Account 1 ── N Queue.accountId Queue N ── M Department via Queue.departments[] (see smarttalks_departments) — departments define the candidate pool Queue references Users indirectly via each Department's users — the distribution algorithms resolve the pool at dispatch time, not at queue write time expiration.queue routes can target other Queues, Units (smarttalks_units), Tags (smarttalks_tags), or flow/block ids ACTIONS get { id } — fetch one queue list { query, project?, options? } — Mongo filter. Common: { isActive: true } — queues accepting new interactions { departments: "<deptId>" } — queues staffed by a department { "roles.distribution": "balance" } — all balance-routed queues { "roles.distribution": "sequential" } — all round-robin queues create { payload } — required: label. Typically: departments[], roles{…}, expiration{…}. groupId/accountId server-injected. update { id, payload } — mutate any field. NOTE: changing roles.distribution takes effect on the next interaction routed; in-flight interactions already assigned are not reshuffled. delete { id } — soft delete (sets status:false) ADDITIONAL (NOT EXPOSED in v1) POST /v1/queues/expiration/:interactionId — resolve an expiration job GET /v1/queues/available/users — list available users GET /v1/queues/:id/users — list users eligible for a specific queue POST /v1/queues/options — aggregation for select-box rendering These may be added later as intent-level tools.

smarttalks_queues_manage

ChatGPT
Queues — INTERACTION ROUTING QUEUES. A queue belongs to ONE Group and ONE Account and fans out to MANY Departments (via departments[]). Queues drive how live interactions are distributed to users. FIELDS _id (ObjectId), label (string, required) — display name caption (string) — short description groupId (ObjectId, required, server-injected), accountId (ObjectId, required, server-injected) isActive (boolean, default true) — whether the queue actively receives new interactions status (boolean, default true) — soft-delete departments[] (ObjectId[]) — Departments that staff this queue (see smarttalks_departments); users in those departments are the candidate pool for routing roles (object) — routing behaviour: roles.isCheckIn (boolean, default true) — require user check-in roles.isForcePass (boolean, default false) — see DISTRIBUTION STRATEGIES below (controls whether offline/away users are still candidates) roles.isQueue (boolean, default true) — treat as a queue (vs direct-attend) roles.positionTransmission (number, default 0) — how often the queue position is broadcast to the client (seconds) roles.maximumPerAttendant (number, default 10) — per-user concurrent-interaction cap for THIS queue (used as the capacity ceiling when a user has no personal maxConcurrentCalls override) roles.distribution (enum: "balance" | "sequential" | "open", default "open") — routing strategy; see DISTRIBUTION STRATEGIES below for the behavioural differences roles.disableAudio (boolean), roles.disableAnnex (boolean), roles.disableEmojis (boolean) roles.automations[] (ObjectId[]) — automation ids expiration (object) — timeout behaviour, three independent sub-policies: expiration.user { isActive, time (s), finishWithoutCsat, message } — idle user timeout expiration.client { isActive, time (s), finishWithoutCsat, message } — idle client timeout expiration.queue { isActive, time (min 5), redirectType: "FLOW"|"UNIT"|"QUEUE"|"ALERT_NOTIFICATION", flowId (ObjectId), blockId (ObjectId), queueId (ObjectId), unitId (ObjectId), units[] (ObjectId[]), tags[] (ObjectId[]), message, notificationMessage } — what to do when an interaction ages out in the queue createdBy, updatedBy (ObjectId — Users), createdAt, updatedAt DISTRIBUTION STRATEGIES — how roles.distribution affects routing All three strategies share a CANDIDATE POOL built by FindUsersByDepartmentService: - Users whose department ∈ queue.departments[] - User.receiveAutomatedAttendances === true (for automated strategies; not required for "open") - If the interaction carries units[], users are further restricted to those who belong to at least one of those units - The caller (auth._id) is excluded from the candidate pool so an agent can't self-assign They also share a CAPACITY CHECK per candidate, priority-ordered: 1. If User.unlimitedConcurrentCalls === true → no cap, always eligible 2. Else if User.maxConcurrentCalls != null → compare to the user's TOTAL active interactions across ALL queues (0 means "cannot receive") 3. Else → compare the user's count in THIS queue only against roles.maximumPerAttendant If no candidate has capacity, the interaction is left in the queue and an expiration job is scheduled via ExpirationService.setQueueJob (which eventually triggers the expiration.queue policy above). • "open" (default) — PASSIVE / BROADCAST No server-side assignment. A socket toast "New client waiting for attendance" is emitted to every user in the candidate pool. The first user to self-pick claims the interaction (manual pull). Best for: small teams, homogeneous skill pool, or when you want agents to choose. Note: roles.isForcePass has no effect on "open" — since no automated filter runs, away/offline users simply don't see the toast. • "balance" — LEAST-LOADED Automatically assigns the interaction to the candidate with the FEWEST active interactions. Sort order (primary → tertiary): 1. attendingTotal across all queues (ascending — lowest workload wins) 2. availability priority: online (1) > away (2) > offline (3) 3. onlineAt ascending (longest currently-online wins the tie) Eligibility default: only availability === "online" users are considered. If roles.isForcePass === true: away/offline users become eligible too (so the queue never stalls when no one is online). Best for: uneven agent productivity, minimize customer wait time. • "sequential" — ROUND-ROBIN Automatically assigns the interaction to the NEXT user in rotation. Rotation state is stored in Redis at the key account:<accountId>:queue:<queueId>[:units:<unit-ids-joined-by-dash>]:position so each (account, queue, units-slice) tuple has its own independent rotation counter. Selection: 1. Read cached position, pick position+1 (wrap to 0 at end of array) 2. If that candidate is at capacity, scan FORWARD for the next one with capacity; if none found, scan from the beginning 3. Write the final position back to cache Within the candidate list, users are sorted by availability then onlineAt (same secondary/tertiary rules as balance) for deterministic ordering. Eligibility default: only availability === "online" users are considered. If roles.isForcePass === true: away/offline users become eligible too. Best for: homogeneous skill pool where fairness ("nobody skipped") matters more than load-optimality. COMPARISON AT A GLANCE | Strategy | Who picks? | Selection basis | State | Fairness | Typical use | | ---------- | ---------- | --------------------- | ------------ | ------------ | ----------- | | open | the user | broadcast to all | none | self-service | small teams, agent choice | | balance | the server | lowest workload | in-request | load-based | uneven productivity, minimize wait | | sequential | the server | round-robin position | Redis cache | position-based | equal skill, strict rotation | RELATIONSHIPS Group 1 ── N Queue.groupId Account 1 ── N Queue.accountId Queue N ── M Department via Queue.departments[] (see smarttalks_departments) — departments define the candidate pool Queue references Users indirectly via each Department's users — the distribution algorithms resolve the pool at dispatch time, not at queue write time expiration.queue routes can target other Queues, Units (smarttalks_units), Tags (smarttalks_tags), or flow/block ids ACTIONS get { id } — fetch one queue list { query, project?, options? } — Mongo filter. Common: { isActive: true } — queues accepting new interactions { departments: "<deptId>" } — queues staffed by a department { "roles.distribution": "balance" } — all balance-routed queues { "roles.distribution": "sequential" } — all round-robin queues create { payload } — required: label. Typically: departments[], roles{…}, expiration{…}. groupId/accountId server-injected. update { id, payload } — mutate any field. NOTE: changing roles.distribution takes effect on the next interaction routed; in-flight interactions already assigned are not reshuffled. delete { id } — soft delete (sets status:false) ADDITIONAL (NOT EXPOSED in v1) POST /v1/queues/expiration/:interactionId — resolve an expiration job GET /v1/queues/available/users — list available users GET /v1/queues/:id/users — list users eligible for a specific queue POST /v1/queues/options — aggregation for select-box rendering These may be added later as intent-level tools.

smarttalks_quick_responses

ChatGPT
Quick responses — CANNED MESSAGE TEMPLATES that operators paste into a live interaction. Scoped to ONE Group + ONE Account; soft-deleted via status. Every change is mirrored into the audits collection for compliance (insert/update/delete events are appended automatically by a server-side change stream + explicit calls in update/delete). IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected from JWT) — IMMUTABLE accountId (ObjectId, required, server-injected from JWT) — IMMUTABLE createdBy (ObjectId, required, server-injected from JWT auth._id) — the user who originally authored the template; cannot be reassigned createdAt (Date, default Date.now) updatedAt (Date, default Date.now) — auto-bumped server-side on save / update / findOneAndUpdate via Mongoose pre-hooks; do NOT set on payload CONTENT title (string, required) — display name shown in the operator-side picker. Min length 1. message (string, required) — the actual message body that gets pasted into the chat. Treat as the user's outgoing message text. command (string, default "") — short slash-style trigger (e.g. "/oi") used by the operator UI for keyboard-shortcut insertion. Free-form; uniqueness is NOT enforced server-side. categoryId (ObjectId, optional) — references a walktalk Category (separate categories collection — categories are NOT yet exposed as an MCP tool; document the linkage but treat the value as opaque from MCP's perspective). attachments[] — array of media references the response carries when pasted. Sub-document shape per item: { _id (ObjectId), type (string), url (string), name (string) } type values are operator-defined media kinds (e.g. "image", "video", "file"); the URL points at the hosted asset. Pass an empty array (or omit) for text-only responses. VISIBILITY / SHARING isHomolog (boolean, default false) — when true, the response is treated as a SHARED template (visible to other operators in the same tenant; semantics enforced by the operator app). When false (default) it's a private response. NOTE: the upstream model defaults this to the STRING 'false' instead of the boolean false due to a typo in the schema — treat as falsy but be aware that older rows may have isHomolog: 'false' in the database. MCP callers should always pass a real boolean. LIFECYCLE status (boolean, default true) — soft-delete flag. The Mongoose pre(/^find/) and pre(/^count/) hooks AUTO-SCOPE every read to status: true UNLESS the caller explicitly sets status in the query — so list will hide soft-deleted rows by default. To list soft-deleted rows, pass { query: { status: false } } (or { status: { $exists: true } } to bypass the auto-scope and see both). DELETE GUARDS (enforced server-side) • soft-delete only: delete flips status to false and stamps updatedAt; the row stays in the collection. • SHARED-template protection: when isHomolog === true, only the ORIGINAL CREATOR (createdBy === auth._id) OR a SmartTalks guider (auth.isGuider === true) is allowed to delete. Anyone else gets HTTP 403 "only the creator or a guider can delete shared quick responses". • not-found: HTTP 404 "quick response not found" when the id doesn't belong to the caller's tenant. AUDIT TRAIL Every CRUD operation is mirrored into the audits collection: • INSERT — written by a Mongo change-stream watcher with { collectionName, operationType, documentId, changes: fullDocument } • UPDATE — written explicitly inside the model's update() with the changed fields plus actor metadata (userId, userEmail, accountId, groupId) • DELETE — written explicitly inside the model's delete() with a full snapshot of the row (so soft-deleted state is recoverable) These rows are READ-only from MCP's perspective; the audits collection is not exposed as an MCP tool today. INDEX Compound: { groupId: 1, accountId: 1, status: 1, _id: 1, createdAt: -1 } RELATIONSHIPS Group 1 ── N QuickResponse.groupId (IMMUTABLE) Account 1 ── N QuickResponse.accountId (IMMUTABLE) User (auth) 1 ── N QuickResponse.createdBy — the operator who authored the template (immutable after create) QuickResponse N ── 1 Category via categoryId (separate Walktalk categories collection — not exposed as a tool yet) ACTIONS get { id } — fetch one quick response by _id. Tenant-scoped server-side. Returns null if not found in tenant. list { query, project? } — Mongo filter; tenant scope and status: true are appended/enforced server-side. Sort is FIXED to { title: 1 } (alphabetical by title) — there is NO options.limit/skip/sort parameter; the upstream model returns ALL matching rows. Common queries: { categoryId: "<catId>" } — by category { isHomolog: true } — only shared templates { command: "/oi" } — by slash-command { title: { $regex: "saudação", $options: "i" } } — substring search { status: false } — soft-deleted rows (bypasses auto-scope) NOTE: options is silently ignored by the upstream — do not rely on limit/skip/sort. create { payload } — required: title, message. Optional: command, categoryId, isHomolog, attachments[]. groupId/accountId/createdBy are server-injected from the JWT — do NOT pass them. update { id, payload, project? } — partial update on any mutable field. updatedAt is auto-bumped. Do NOT pass groupId/accountId/createdBy/createdAt — those are immutable. delete { id } — soft-delete (sets status:false, bumps updatedAt). May fail with 403 on shared (isHomolog:true) responses if the caller is neither the creator nor a guider; see DELETE GUARDS above. NOT EXPOSED • Category management — walktalk_categories is not yet an MCP tool. Treat categoryId as an opaque ObjectId you obtain elsewhere (UI, direct DB, future tool). • Audits collection — not exposed; rows are written automatically and are out of scope for callers.

smarttalks_quick_responses_manage

ChatGPT
Quick responses — CANNED MESSAGE TEMPLATES that operators paste into a live interaction. Scoped to ONE Group + ONE Account; soft-deleted via status. Every change is mirrored into the audits collection for compliance (insert/update/delete events are appended automatically by a server-side change stream + explicit calls in update/delete). IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected from JWT) — IMMUTABLE accountId (ObjectId, required, server-injected from JWT) — IMMUTABLE createdBy (ObjectId, required, server-injected from JWT auth._id) — the user who originally authored the template; cannot be reassigned createdAt (Date, default Date.now) updatedAt (Date, default Date.now) — auto-bumped server-side on save / update / findOneAndUpdate via Mongoose pre-hooks; do NOT set on payload CONTENT title (string, required) — display name shown in the operator-side picker. Min length 1. message (string, required) — the actual message body that gets pasted into the chat. Treat as the user's outgoing message text. command (string, default "") — short slash-style trigger (e.g. "/oi") used by the operator UI for keyboard-shortcut insertion. Free-form; uniqueness is NOT enforced server-side. categoryId (ObjectId, optional) — references a walktalk Category (separate categories collection — categories are NOT yet exposed as an MCP tool; document the linkage but treat the value as opaque from MCP's perspective). attachments[] — array of media references the response carries when pasted. Sub-document shape per item: { _id (ObjectId), type (string), url (string), name (string) } type values are operator-defined media kinds (e.g. "image", "video", "file"); the URL points at the hosted asset. Pass an empty array (or omit) for text-only responses. VISIBILITY / SHARING isHomolog (boolean, default false) — when true, the response is treated as a SHARED template (visible to other operators in the same tenant; semantics enforced by the operator app). When false (default) it's a private response. NOTE: the upstream model defaults this to the STRING 'false' instead of the boolean false due to a typo in the schema — treat as falsy but be aware that older rows may have isHomolog: 'false' in the database. MCP callers should always pass a real boolean. LIFECYCLE status (boolean, default true) — soft-delete flag. The Mongoose pre(/^find/) and pre(/^count/) hooks AUTO-SCOPE every read to status: true UNLESS the caller explicitly sets status in the query — so list will hide soft-deleted rows by default. To list soft-deleted rows, pass { query: { status: false } } (or { status: { $exists: true } } to bypass the auto-scope and see both). DELETE GUARDS (enforced server-side) • soft-delete only: delete flips status to false and stamps updatedAt; the row stays in the collection. • SHARED-template protection: when isHomolog === true, only the ORIGINAL CREATOR (createdBy === auth._id) OR a SmartTalks guider (auth.isGuider === true) is allowed to delete. Anyone else gets HTTP 403 "only the creator or a guider can delete shared quick responses". • not-found: HTTP 404 "quick response not found" when the id doesn't belong to the caller's tenant. AUDIT TRAIL Every CRUD operation is mirrored into the audits collection: • INSERT — written by a Mongo change-stream watcher with { collectionName, operationType, documentId, changes: fullDocument } • UPDATE — written explicitly inside the model's update() with the changed fields plus actor metadata (userId, userEmail, accountId, groupId) • DELETE — written explicitly inside the model's delete() with a full snapshot of the row (so soft-deleted state is recoverable) These rows are READ-only from MCP's perspective; the audits collection is not exposed as an MCP tool today. INDEX Compound: { groupId: 1, accountId: 1, status: 1, _id: 1, createdAt: -1 } RELATIONSHIPS Group 1 ── N QuickResponse.groupId (IMMUTABLE) Account 1 ── N QuickResponse.accountId (IMMUTABLE) User (auth) 1 ── N QuickResponse.createdBy — the operator who authored the template (immutable after create) QuickResponse N ── 1 Category via categoryId (separate Walktalk categories collection — not exposed as a tool yet) ACTIONS get { id } — fetch one quick response by _id. Tenant-scoped server-side. Returns null if not found in tenant. list { query, project? } — Mongo filter; tenant scope and status: true are appended/enforced server-side. Sort is FIXED to { title: 1 } (alphabetical by title) — there is NO options.limit/skip/sort parameter; the upstream model returns ALL matching rows. Common queries: { categoryId: "<catId>" } — by category { isHomolog: true } — only shared templates { command: "/oi" } — by slash-command { title: { $regex: "saudação", $options: "i" } } — substring search { status: false } — soft-deleted rows (bypasses auto-scope) NOTE: options is silently ignored by the upstream — do not rely on limit/skip/sort. create { payload } — required: title, message. Optional: command, categoryId, isHomolog, attachments[]. groupId/accountId/createdBy are server-injected from the JWT — do NOT pass them. update { id, payload, project? } — partial update on any mutable field. updatedAt is auto-bumped. Do NOT pass groupId/accountId/createdBy/createdAt — those are immutable. delete { id } — soft-delete (sets status:false, bumps updatedAt). May fail with 403 on shared (isHomolog:true) responses if the caller is neither the creator nor a guider; see DELETE GUARDS above. NOT EXPOSED • Category management — walktalk_categories is not yet an MCP tool. Treat categoryId as an opaque ObjectId you obtain elsewhere (UI, direct DB, future tool). • Audits collection — not exposed; rows are written automatically and are out of scope for callers.

smarttalks_schedule_pages

ChatGPT
Schedule pages — BOOKING PAGE MANAGEMENT. A schedule page is a hosted public landing page that groups one or more appointment schedules under a single URL. Scoped to one Group + Account; reads and writes are auto-scoped to the caller's JWT claims. INPUT SHAPE { action, ...args } IDENTITY & TENANCY (applies to every record) _id (ObjectId) — primary key groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically PAGE IDENTITY title (string, required) — display name shown at the top of the hosted page slug (string, required, GLOBALLY UNIQUE, IMMUTABLE) — URL path segment used to form the public booking URL. Format: lowercase alphanumeric with hyphens only (e.g. "my-hotel", "spa-booking-2026"). Pattern: /^[a-z0-9]+(?:-[a-z0-9]+)*$/ Uniqueness is enforced across ALL tenants, not just within a group — choose slugs carefully. IMMUTABLE: the server silently ignores slug on update. Choose the correct slug at creation time. description (string) — optional paragraph shown below the title on the hosted page APPEARANCE logo (string, URL) — logo/image URL displayed on the page; nullable (pass null to remove) backgroundColor (string, default "#1a1a2e") — 6-digit hex only (e.g. "#ffffff"). Must match /^#[0-9a-fA-F]{6}$/. Widen the pattern if alpha values are needed later. layout (enum: "list" | "cards", default "cards") "cards" — grid of card tiles; best for browsable catalogs "list" — vertical list; better for long text descriptions or sequential flows VISIBILITY isActive (boolean, default true) — when false the page returns 404 / 403 on the public URL; use this to temporarily hide a page without deleting it SCHEDULE GROUPING categories[] — optional ordered grouping of schedule ids: id (string, required) — caller-managed stable identifier; NOT an ObjectId name (string, required) — display label for the category section scheduleIds (string[], default []) — ordered ids of schedules in this category order (number int, default 0) — display order; lower values appear first uncategorizedScheduleIds (string[], default []) — schedule ids not assigned to any category; rendered as a flat ungrouped section below any categories NOTE: update replaces the ENTIRE categories / uncategorizedScheduleIds arrays ($set semantics). To add a single schedule, read the current document first and re-send the full merged arrays. INDEXES { slug: 1 } UNIQUE — slug lookup is fast; always use slug for URL-based lookups { groupId: 1, accountId: 1 } — tenant scope index; all list queries hit this RELATIONSHIPS Group 1 ── N SchedulePage.groupId Account 1 ── N SchedulePage.accountId Schedule N ── M via categories[].scheduleIds + uncategorizedScheduleIds (referenced by id string, not ObjectId) ACTIONS list { limit?, skip?, sort? } Returns { items[], count }. Defaults: limit=10, skip=0, sort="-createdAt". sort uses Mongoose string format: prefix "-" for descending (e.g. "-title", "createdAt"). get { id } Full document by ObjectId. create { title, slug, description?, logo?, backgroundColor?, layout?, isActive?, categories?, uncategorizedScheduleIds? } slug is GLOBALLY UNIQUE and IMMUTABLE — choose carefully. Returns the created schedule page document. update { id, title?, description?, logo?, backgroundColor?, layout?, isActive?, categories?, uncategorizedScheduleIds? } $set patch — only provided fields are updated. slug is silently ignored if sent. categories and uncategorizedScheduleIds are FULLY REPLACED if provided. Returns the updated document. delete { id } PERMANENT hard-delete. No soft-delete or recovery path. Returns { status: "ok" } with no result body on success. EXAMPLES list { action: "list", limit: 20, sort: "-createdAt" } get { action: "get", id: "<pageId>" } create { action: "create", title: "Hotel Booking", slug: "hotel-booking-2026", layout: "cards", backgroundColor: "#1a1a2e", uncategorizedScheduleIds: ["<scheduleId1>", "<scheduleId2>"] } update { action: "update", id: "<pageId>", isActive: false } update { action: "update", id: "<pageId>", categories: [{ id: "cat-1", name: "Rooms", scheduleIds: ["<s1>"], order: 0 }], uncategorizedScheduleIds: [] } delete { action: "delete", id: "<pageId>" }

smarttalks_schedule_pages_manage

ChatGPT
Schedule pages — BOOKING PAGE MANAGEMENT. A schedule page is a hosted public landing page that groups one or more appointment schedules under a single URL. Scoped to one Group + Account; reads and writes are auto-scoped to the caller's JWT claims. INPUT SHAPE { action, ...args } IDENTITY & TENANCY (applies to every record) _id (ObjectId) — primary key groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically PAGE IDENTITY title (string, required) — display name shown at the top of the hosted page slug (string, required, GLOBALLY UNIQUE, IMMUTABLE) — URL path segment used to form the public booking URL. Format: lowercase alphanumeric with hyphens only (e.g. "my-hotel", "spa-booking-2026"). Pattern: /^[a-z0-9]+(?:-[a-z0-9]+)*$/ Uniqueness is enforced across ALL tenants, not just within a group — choose slugs carefully. IMMUTABLE: the server silently ignores slug on update. Choose the correct slug at creation time. description (string) — optional paragraph shown below the title on the hosted page APPEARANCE logo (string, URL) — logo/image URL displayed on the page; nullable (pass null to remove) backgroundColor (string, default "#1a1a2e") — 6-digit hex only (e.g. "#ffffff"). Must match /^#[0-9a-fA-F]{6}$/. Widen the pattern if alpha values are needed later. layout (enum: "list" | "cards", default "cards") "cards" — grid of card tiles; best for browsable catalogs "list" — vertical list; better for long text descriptions or sequential flows VISIBILITY isActive (boolean, default true) — when false the page returns 404 / 403 on the public URL; use this to temporarily hide a page without deleting it SCHEDULE GROUPING categories[] — optional ordered grouping of schedule ids: id (string, required) — caller-managed stable identifier; NOT an ObjectId name (string, required) — display label for the category section scheduleIds (string[], default []) — ordered ids of schedules in this category order (number int, default 0) — display order; lower values appear first uncategorizedScheduleIds (string[], default []) — schedule ids not assigned to any category; rendered as a flat ungrouped section below any categories NOTE: update replaces the ENTIRE categories / uncategorizedScheduleIds arrays ($set semantics). To add a single schedule, read the current document first and re-send the full merged arrays. INDEXES { slug: 1 } UNIQUE — slug lookup is fast; always use slug for URL-based lookups { groupId: 1, accountId: 1 } — tenant scope index; all list queries hit this RELATIONSHIPS Group 1 ── N SchedulePage.groupId Account 1 ── N SchedulePage.accountId Schedule N ── M via categories[].scheduleIds + uncategorizedScheduleIds (referenced by id string, not ObjectId) ACTIONS list { limit?, skip?, sort? } Returns { items[], count }. Defaults: limit=10, skip=0, sort="-createdAt". sort uses Mongoose string format: prefix "-" for descending (e.g. "-title", "createdAt"). get { id } Full document by ObjectId. create { title, slug, description?, logo?, backgroundColor?, layout?, isActive?, categories?, uncategorizedScheduleIds? } slug is GLOBALLY UNIQUE and IMMUTABLE — choose carefully. Returns the created schedule page document. update { id, title?, description?, logo?, backgroundColor?, layout?, isActive?, categories?, uncategorizedScheduleIds? } $set patch — only provided fields are updated. slug is silently ignored if sent. categories and uncategorizedScheduleIds are FULLY REPLACED if provided. Returns the updated document. delete { id } PERMANENT hard-delete. No soft-delete or recovery path. Returns { status: "ok" } with no result body on success. EXAMPLES list { action: "list", limit: 20, sort: "-createdAt" } get { action: "get", id: "<pageId>" } create { action: "create", title: "Hotel Booking", slug: "hotel-booking-2026", layout: "cards", backgroundColor: "#1a1a2e", uncategorizedScheduleIds: ["<scheduleId1>", "<scheduleId2>"] } update { action: "update", id: "<pageId>", isActive: false } update { action: "update", id: "<pageId>", categories: [{ id: "cat-1", name: "Rooms", scheduleIds: ["<s1>"], order: 0 }], uncategorizedScheduleIds: [] } delete { action: "delete", id: "<pageId>" }

smarttalks_schedules

ChatGPT
Schedules — booking calendars that define time SLOTS and drive APPOINTMENTS for contacts. Scoped to one Group + Account; every read/write is auto-scoped to the caller's JWT claims. Mounted under /v1/schedules. INPUT SHAPE { resource: "schedule", action, ...args } IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account units[] (ObjectId[]) — org units this schedule is scoped to; list queries filter by caller's units when present createdBy, updatedBy, deletedBy (ObjectId) — audit trail createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null) — soft-delete; null = active, present = trashed TYPE & IDENTITY type (enum, required): "REGULAR" | "RENTAL" | "TABLE" — controls which capacity fields and sub-resources are active title (string, required) description (string) slug (string, required, unique) — url-safe identifier generated via nanoid(7); used for public booking pages avatar (string) — URL to the schedule's cover image tags (string[]) — free-text labels BOOKING WINDOW isActive (boolean, default false) — schedule is open for bookings isPublic (boolean, default false) — exposed on the public booking page timezone (string) — IANA tz for slot computation (e.g. "America/Sao_Paulo") minimumAppointmentNotice (number, default 120) — minutes in advance required to book maxNoticeDays (number, default 90) — how many days ahead can be booked periodType (enum, default "UNLIMITED"): "UNLIMITED" | "RANGE" periodStartDate, periodEndDate (Date) — active booking window when periodType="RANGE" requiresConfirmation (boolean, default false) — newly created appointments land in PENDING status successRedirectUrl (string) — redirect after successful booking CAPACITY canExceedAvailabilityCapacity (boolean, default false) — allow overbooking capacityLimitationType (enum, default "by_people"): "by_people" | "by_reservations" maxReservationsPerSlot (number | null) — max reservations per slot when capacityLimitationType="by_reservations" maxCapacityPerAppointment (number, default 1) — max people per single appointment maxItemsPerAppointment (number) — max rental items per appointment (RENTAL type) showSlotsCount (boolean, default false) — expose remaining capacity in slot responses hideDuration (boolean, default true) — hide slot duration on public booking page AVAILABILITIES (array) — define WHEN slots exist Each entry: type (enum): "recurring" | "specific" weekdays (string[]) — active for "recurring": "MONDAY"|"TUESDAY"|"WEDNESDAY"|"THURSDAY"|"FRIDAY"|"SATURDAY"|"SUNDAY" specificDates (string[]) — YYYY-MM-DD list for "specific" type exceptions (string[]) — YYYY-MM-DD dates excluded from a recurring rule isActive (boolean, default false) startTime, endTime (Date) — wall-clock bounds for the availability window duration (number, required) — default slot duration in minutes multipleDurations (number[]) — offer multiple durations on the same window maxCapacity (number) — max people for this availability window FORMS (array) — custom booking form fields Each field: { id (nanoid), label, type, placeholder, options[], required, disabled, min, max, textInfo, mediaInfo, condition } type enum: "short_text"|"long_text"|"number"|"checkbox"|"checkbox_group"|"radio"|"radio_group"|"select"|"multi_select"|"email"|"multi_email"|"url"|"phone"|"cpf"|"date"|"time"|"text_info"|"media_info"|"rating" condition: { formId, operator ("EQUAL"|"NOT_EQUAL"|"GREATER_THAN"|"GREATER_THAN_EQUAL"|"LESS_THAN"|"LESS_THAN_EQUAL"), value } CONDITIONS (array) — server-side booking pre-conditions Each: { type: "SDM_FIELD", parameters: { entityFieldId, operator, value } } operator enum: "EQUAL"|"NOT_EQUAL"|"GREATER_THAN"|"GREATER_THAN_EQUAL"|"LESS_THAN"|"LESS_THAN_EQUAL"|"IS_EMPTY"|"IS_NOT_EMPTY"|"CONTAINS"|"NOT_CONTAINS"|"IS_IN"|"IS_NOT_IN"|"IS_UNIQUE" NOTIFICATIONS (sub-object) method (enum, default "emails"): "emails" | "users" | "both" emails (string[], default []) — recipient emails userEmails (string[], default []) — user-based recipient emails WHATSAPP CONFIRMATION (sub-object) active (boolean, default false) — send WA confirmation after booking messageType (enum, default "template"): "template" | "message" channelId (ObjectId | null) — WA channel to use unitId (ObjectId | null) — org unit scope for the WA channel templateId (string | null) — HSM template id message (string | null) — free-text message when messageType="message" variables.header / variables.body / variables.button (Map<string,string>) — template variable maps tags (string[], default []) — tags to apply to the resulting WA interaction sendTiming (enum, default "immediate"): "immediate" | "delayed" delayMinutes (number | null, min 1, max 1440) — delay in minutes when sendTiming="delayed" RECURRENCE (sub-object) — controls recurring appointment configuration mode (enum, default "none"): "none" | "required" | "optional" allowedFrequencies (string[], default ["weekly","biweekly","monthly"]): subset of "weekly"|"biweekly"|"monthly" maxOccurrences (number, default 10, min 1, max 10) — hard cap on the series length maxDate (Date | null, default null) — optional series end date; null means no date limit maxOccurrences and maxDate are complementary, NOT mutually exclusive: a series ends as soon as EITHER limit is reached (whichever comes first). notifyLastOccurrence (boolean, default true) notifyLastOccurrenceEmails (string[], default []) RENTAL ITEMS (array, type="RENTAL" schedules only) Each item: { id (nanoid), isActive, label (required), description, image, price, currency ("BRL"|"USD"|"EUR"|"ARC"|"UYU"|"PYG"), quantity (required), attributes (Map<string,string>) } TABLES (array, type="TABLE" schedules only) Each table: { id (nanoid), isActive, seats (default 2), quantity, label (required), description } INDEXES (shape queries to hit these) (groupId, accountId, units, createdAt) (groupId, accountId, createdAt) Always include groupId + accountId (server-injected from JWT). RELATIONSHIPS Group 1 ── N Schedule.groupId Account 1 ── N Schedule.accountId Schedule 1 ── N Appointment.scheduleId Schedule 1 ── N Conflict.scheduleId ACTIONS schedule.list { search?, orderBy?, order?, skip?, limit?, trash? } Lists schedules for the caller's account/group. search — full-text against title, description, slug, id, _id. trash: true — returns trashed (deleted) schedules; false (default) — active only. options: { orderBy (default "createdAt"), order ("asc"|"desc"), skip, limit } Returns { items: Schedule[], count: number }. schedule.get { id } Fetch one schedule by ObjectId. schedule.create { payload } Required in payload: title, type. Server auto-generates: slug (nanoid 7), id (counter), timestamps. Optional: all other fields above. schedule.update { id, payload } Patch any mutable field. Partial update — only include changed keys. schedule.delete { id } Soft-delete (sets deletedAt to now). Hidden from subsequent list queries. schedule.slots { id, date?, duration?, month? } GET /:id/slots — compute available booking slots for the schedule. date — ISO date string to compute slots from (defaults to today). duration — filter slots matching a specific duration (minutes). month — YYYY-MM to load the entire month's slot map. Returns slot availability matrix respecting capacity, exceptions and existing appointments (SCHEDULED|CONFIRMED|PENDING). schedule.copy { id, payload? } POST /:id/copy — duplicate a schedule. payload can override fields on the new copy. Returns the new Schedule (201). Response Location header set to the new schedule URL. schedule.planner { startDate, endDate, scheduleIds?, status? } GET /planner — calendar-style aggregation of appointments across schedules. startDate, endDate — ISO date strings (required). scheduleIds — comma-separated ObjectIds to restrict schedules. status — comma-separated appointment statuses to filter. Returns { summary: { totalAppointments, totalPeople }, schedules: [], dates: [] }. Results are cached server-side (5 min TTL). schedule.stats { id, date? } GET /:scheduleId/stats — daily and monthly appointment statistics for a single schedule. date — ISO date string; defaults to today if omitted. EXAMPLES schedule.list { resource: "schedule", action: "list", limit: 20 } schedule.get { resource: "schedule", action: "get", id: "<scheduleId>" } schedule.create { resource: "schedule", action: "create", payload: { title: "Consulta", type: "REGULAR", timezone: "America/Sao_Paulo" } } schedule.update { resource: "schedule", action: "update", id: "<scheduleId>", payload: { isActive: true } } schedule.delete { resource: "schedule", action: "delete", id: "<scheduleId>" } schedule.slots { resource: "schedule", action: "slots", id: "<scheduleId>", month: "2026-05" } schedule.copy { resource: "schedule", action: "copy", id: "<scheduleId>", payload: { title: "Consulta (cópia)" } } schedule.planner { resource: "schedule", action: "planner", startDate: "2026-05-01", endDate: "2026-05-31" } schedule.stats { resource: "schedule", action: "stats", id: "<scheduleId>", date: "2026-05-01" }

smarttalks_schedules_manage

ChatGPT
Schedules — booking calendars that define time SLOTS and drive APPOINTMENTS for contacts. Scoped to one Group + Account; every read/write is auto-scoped to the caller's JWT claims. Mounted under /v1/schedules. INPUT SHAPE { resource: "schedule", action, ...args } IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account units[] (ObjectId[]) — org units this schedule is scoped to; list queries filter by caller's units when present createdBy, updatedBy, deletedBy (ObjectId) — audit trail createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null) — soft-delete; null = active, present = trashed TYPE & IDENTITY type (enum, required): "REGULAR" | "RENTAL" | "TABLE" — controls which capacity fields and sub-resources are active title (string, required) description (string) slug (string, required, unique) — url-safe identifier generated via nanoid(7); used for public booking pages avatar (string) — URL to the schedule's cover image tags (string[]) — free-text labels BOOKING WINDOW isActive (boolean, default false) — schedule is open for bookings isPublic (boolean, default false) — exposed on the public booking page timezone (string) — IANA tz for slot computation (e.g. "America/Sao_Paulo") minimumAppointmentNotice (number, default 120) — minutes in advance required to book maxNoticeDays (number, default 90) — how many days ahead can be booked periodType (enum, default "UNLIMITED"): "UNLIMITED" | "RANGE" periodStartDate, periodEndDate (Date) — active booking window when periodType="RANGE" requiresConfirmation (boolean, default false) — newly created appointments land in PENDING status successRedirectUrl (string) — redirect after successful booking CAPACITY canExceedAvailabilityCapacity (boolean, default false) — allow overbooking capacityLimitationType (enum, default "by_people"): "by_people" | "by_reservations" maxReservationsPerSlot (number | null) — max reservations per slot when capacityLimitationType="by_reservations" maxCapacityPerAppointment (number, default 1) — max people per single appointment maxItemsPerAppointment (number) — max rental items per appointment (RENTAL type) showSlotsCount (boolean, default false) — expose remaining capacity in slot responses hideDuration (boolean, default true) — hide slot duration on public booking page AVAILABILITIES (array) — define WHEN slots exist Each entry: type (enum): "recurring" | "specific" weekdays (string[]) — active for "recurring": "MONDAY"|"TUESDAY"|"WEDNESDAY"|"THURSDAY"|"FRIDAY"|"SATURDAY"|"SUNDAY" specificDates (string[]) — YYYY-MM-DD list for "specific" type exceptions (string[]) — YYYY-MM-DD dates excluded from a recurring rule isActive (boolean, default false) startTime, endTime (Date) — wall-clock bounds for the availability window duration (number, required) — default slot duration in minutes multipleDurations (number[]) — offer multiple durations on the same window maxCapacity (number) — max people for this availability window FORMS (array) — custom booking form fields Each field: { id (nanoid), label, type, placeholder, options[], required, disabled, min, max, textInfo, mediaInfo, condition } type enum: "short_text"|"long_text"|"number"|"checkbox"|"checkbox_group"|"radio"|"radio_group"|"select"|"multi_select"|"email"|"multi_email"|"url"|"phone"|"cpf"|"date"|"time"|"text_info"|"media_info"|"rating" condition: { formId, operator ("EQUAL"|"NOT_EQUAL"|"GREATER_THAN"|"GREATER_THAN_EQUAL"|"LESS_THAN"|"LESS_THAN_EQUAL"), value } CONDITIONS (array) — server-side booking pre-conditions Each: { type: "SDM_FIELD", parameters: { entityFieldId, operator, value } } operator enum: "EQUAL"|"NOT_EQUAL"|"GREATER_THAN"|"GREATER_THAN_EQUAL"|"LESS_THAN"|"LESS_THAN_EQUAL"|"IS_EMPTY"|"IS_NOT_EMPTY"|"CONTAINS"|"NOT_CONTAINS"|"IS_IN"|"IS_NOT_IN"|"IS_UNIQUE" NOTIFICATIONS (sub-object) method (enum, default "emails"): "emails" | "users" | "both" emails (string[], default []) — recipient emails userEmails (string[], default []) — user-based recipient emails WHATSAPP CONFIRMATION (sub-object) active (boolean, default false) — send WA confirmation after booking messageType (enum, default "template"): "template" | "message" channelId (ObjectId | null) — WA channel to use unitId (ObjectId | null) — org unit scope for the WA channel templateId (string | null) — HSM template id message (string | null) — free-text message when messageType="message" variables.header / variables.body / variables.button (Map<string,string>) — template variable maps tags (string[], default []) — tags to apply to the resulting WA interaction sendTiming (enum, default "immediate"): "immediate" | "delayed" delayMinutes (number | null, min 1, max 1440) — delay in minutes when sendTiming="delayed" RECURRENCE (sub-object) — controls recurring appointment configuration mode (enum, default "none"): "none" | "required" | "optional" allowedFrequencies (string[], default ["weekly","biweekly","monthly"]): subset of "weekly"|"biweekly"|"monthly" maxOccurrences (number, default 10, min 1, max 10) — hard cap on the series length maxDate (Date | null, default null) — optional series end date; null means no date limit maxOccurrences and maxDate are complementary, NOT mutually exclusive: a series ends as soon as EITHER limit is reached (whichever comes first). notifyLastOccurrence (boolean, default true) notifyLastOccurrenceEmails (string[], default []) RENTAL ITEMS (array, type="RENTAL" schedules only) Each item: { id (nanoid), isActive, label (required), description, image, price, currency ("BRL"|"USD"|"EUR"|"ARC"|"UYU"|"PYG"), quantity (required), attributes (Map<string,string>) } TABLES (array, type="TABLE" schedules only) Each table: { id (nanoid), isActive, seats (default 2), quantity, label (required), description } INDEXES (shape queries to hit these) (groupId, accountId, units, createdAt) (groupId, accountId, createdAt) Always include groupId + accountId (server-injected from JWT). RELATIONSHIPS Group 1 ── N Schedule.groupId Account 1 ── N Schedule.accountId Schedule 1 ── N Appointment.scheduleId Schedule 1 ── N Conflict.scheduleId ACTIONS schedule.list { search?, orderBy?, order?, skip?, limit?, trash? } Lists schedules for the caller's account/group. search — full-text against title, description, slug, id, _id. trash: true — returns trashed (deleted) schedules; false (default) — active only. options: { orderBy (default "createdAt"), order ("asc"|"desc"), skip, limit } Returns { items: Schedule[], count: number }. schedule.get { id } Fetch one schedule by ObjectId. schedule.create { payload } Required in payload: title, type. Server auto-generates: slug (nanoid 7), id (counter), timestamps. Optional: all other fields above. schedule.update { id, payload } Patch any mutable field. Partial update — only include changed keys. schedule.delete { id } Soft-delete (sets deletedAt to now). Hidden from subsequent list queries. schedule.slots { id, date?, duration?, month? } GET /:id/slots — compute available booking slots for the schedule. date — ISO date string to compute slots from (defaults to today). duration — filter slots matching a specific duration (minutes). month — YYYY-MM to load the entire month's slot map. Returns slot availability matrix respecting capacity, exceptions and existing appointments (SCHEDULED|CONFIRMED|PENDING). schedule.copy { id, payload? } POST /:id/copy — duplicate a schedule. payload can override fields on the new copy. Returns the new Schedule (201). Response Location header set to the new schedule URL. schedule.planner { startDate, endDate, scheduleIds?, status? } GET /planner — calendar-style aggregation of appointments across schedules. startDate, endDate — ISO date strings (required). scheduleIds — comma-separated ObjectIds to restrict schedules. status — comma-separated appointment statuses to filter. Returns { summary: { totalAppointments, totalPeople }, schedules: [], dates: [] }. Results are cached server-side (5 min TTL). schedule.stats { id, date? } GET /:scheduleId/stats — daily and monthly appointment statistics for a single schedule. date — ISO date string; defaults to today if omitted. EXAMPLES schedule.list { resource: "schedule", action: "list", limit: 20 } schedule.get { resource: "schedule", action: "get", id: "<scheduleId>" } schedule.create { resource: "schedule", action: "create", payload: { title: "Consulta", type: "REGULAR", timezone: "America/Sao_Paulo" } } schedule.update { resource: "schedule", action: "update", id: "<scheduleId>", payload: { isActive: true } } schedule.delete { resource: "schedule", action: "delete", id: "<scheduleId>" } schedule.slots { resource: "schedule", action: "slots", id: "<scheduleId>", month: "2026-05" } schedule.copy { resource: "schedule", action: "copy", id: "<scheduleId>", payload: { title: "Consulta (cópia)" } } schedule.planner { resource: "schedule", action: "planner", startDate: "2026-05-01", endDate: "2026-05-31" } schedule.stats { resource: "schedule", action: "stats", id: "<scheduleId>", date: "2026-05-01" }

smarttalks_session

ChatGPT
Session facade. Manages the caller's account context for subsequent tool calls. INPUT SHAPE { action: "list" | "switch" | "reset", groupId?, accountId? } ACTIONS list — fetch the profile accounts the caller can operate on (GET /v1/profile/accounts). switch — mirrors what the browser App sends to POST /v1/login/refresh with { payload: { groupId, accountId } } and returns a fresh JWT. Non-guiders can only switch to accounts present in their accounts[]. The new token is cached server-side (keyed by a hash of the caller's original JWT) and auto-injected on later tool calls. The raw token is NOT returned to the client. reset — clears the cached token; subsequent calls go back to using the caller's original JWT.

smarttalks_session_manage

ChatGPT
Session facade. Manages the caller's account context for subsequent tool calls. INPUT SHAPE { action: "list" | "switch" | "reset", groupId?, accountId? } ACTIONS list — fetch the profile accounts the caller can operate on (GET /v1/profile/accounts). switch — mirrors what the browser App sends to POST /v1/login/refresh with { payload: { groupId, accountId } } and returns a fresh JWT. Non-guiders can only switch to accounts present in their accounts[]. The new token is cached server-side (keyed by a hash of the caller's original JWT) and auto-injected on later tool calls. The raw token is NOT returned to the client. reset — clears the cached token; subsequent calls go back to using the caller's original JWT.

smarttalks_skills

ChatGPT
Skills (v2) — REUSABLE SKILL BLOCKS referenced by Agents (preprocessing/postprocessing pipelines) and Flows. Each skill describes a discrete unit of logic — AI prompt, HTTP/script, contact/entity operation, livechat handoff, email, register, RAG, media, interactive menu, or smart_content. Scoped to one Group + Account. INPUT SHAPE { action, ...args } IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters status:true createdAt, updatedAt (Date) — managed automatically FIELDS label (string, REQUIRED) — human-readable name description (string) — short description type (string, REQUIRED enum): "script" | "ai" | "contact" | "entity" | "livechat" | "unit" | "email" | "register" | "rag" | "media" | "interactive" | "smart_content" reference (string) — optional opaque reference id used by the engine to bind this skill to an external resource code (string) — JS function body (primarily for type:"script" / "ai") configs (object) — model configs (LLM tuning). Shape owned by agents pipeline. entityId (string) — for type:"entity" input (string) — for type:"ai" queueId (string) — for type:"livechat" unitId (string) — for type:"unit" modelId (string) — for type:"ai" (which LLM) mediaUrl (string), mediaType (enum: "image" | "video" | "audio" | "document") — for type:"media" interactive (object) — for type:"interactive" { option, message, type: "list" | "button", options[]: { label, description }, saveResponse (bool, default false), saveResponseFieldId, evidenceResponse (bool, default false) } registerFields[] — for type:"register" [{ label, fieldId (required), access: "contact" | "clientId" | "interactionId" | "variable" | "date" | "time" | "datetime", value }] conditions[][] — nested execution predicates (per-context, evaluated at runtime) [[{ reference, operator: "===" | "!==" | ">" | ">=" | "<" | "<=" | "?" | ":" | "startsWith" | "endsWith" | "includes", operation: "units" | "channel" | "contact" | "date" | "variable" | "input", value }]] RELATIONSHIPS Group 1 ── N Skill.groupId Account 1 ── N Skill.accountId Skill N ── M Agent.preprocessingSkills[], Agent.postprocessingSkills[] (see smarttalks_agents) ACTIONS list { query? } GET /v2/skills with querystring. query supports: { skip, limit (1..100), sort: "-createdAt"|"+createdAt"|"-updatedAt"|"+updatedAt"|"-id"|"+id", _id, id, label, description }. NOTE: the backend does NOT accept arbitrary Mongo filters here — only the listed fields. Use get for a specific id. get { id } — GET /v2/skills/:id — single skill (full document) create { payload } — POST /v2/skills — payload keys: label (required), type (required, enum above), plus any of the type-specific fields described in FIELDS. groupId/accountId server-injected. update { id, payload } — PATCH /v2/skills/:id — patch any mutable field. type is NOT accepted on update (backend schema omits it). delete { id } — DELETE /v2/skills/:id — soft-delete (sets status:false) EXAMPLES list { query: { limit: 20, sort: "-createdAt" } } get { id: "<skillId>" } create { payload: { label: "Classify intent", type: "ai", input: "{{message}}", modelId: "<modelId>", configs: {...} } } update { id: "<skillId>", payload: { label: "Classify intent (v2)" } }

smarttalks_skills_manage

ChatGPT
Skills (v2) — REUSABLE SKILL BLOCKS referenced by Agents (preprocessing/postprocessing pipelines) and Flows. Each skill describes a discrete unit of logic — AI prompt, HTTP/script, contact/entity operation, livechat handoff, email, register, RAG, media, interactive menu, or smart_content. Scoped to one Group + Account. INPUT SHAPE { action, ...args } IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account status (boolean, default true) — soft-delete; pre-find middleware filters status:true createdAt, updatedAt (Date) — managed automatically FIELDS label (string, REQUIRED) — human-readable name description (string) — short description type (string, REQUIRED enum): "script" | "ai" | "contact" | "entity" | "livechat" | "unit" | "email" | "register" | "rag" | "media" | "interactive" | "smart_content" reference (string) — optional opaque reference id used by the engine to bind this skill to an external resource code (string) — JS function body (primarily for type:"script" / "ai") configs (object) — model configs (LLM tuning). Shape owned by agents pipeline. entityId (string) — for type:"entity" input (string) — for type:"ai" queueId (string) — for type:"livechat" unitId (string) — for type:"unit" modelId (string) — for type:"ai" (which LLM) mediaUrl (string), mediaType (enum: "image" | "video" | "audio" | "document") — for type:"media" interactive (object) — for type:"interactive" { option, message, type: "list" | "button", options[]: { label, description }, saveResponse (bool, default false), saveResponseFieldId, evidenceResponse (bool, default false) } registerFields[] — for type:"register" [{ label, fieldId (required), access: "contact" | "clientId" | "interactionId" | "variable" | "date" | "time" | "datetime", value }] conditions[][] — nested execution predicates (per-context, evaluated at runtime) [[{ reference, operator: "===" | "!==" | ">" | ">=" | "<" | "<=" | "?" | ":" | "startsWith" | "endsWith" | "includes", operation: "units" | "channel" | "contact" | "date" | "variable" | "input", value }]] RELATIONSHIPS Group 1 ── N Skill.groupId Account 1 ── N Skill.accountId Skill N ── M Agent.preprocessingSkills[], Agent.postprocessingSkills[] (see smarttalks_agents) ACTIONS list { query? } GET /v2/skills with querystring. query supports: { skip, limit (1..100), sort: "-createdAt"|"+createdAt"|"-updatedAt"|"+updatedAt"|"-id"|"+id", _id, id, label, description }. NOTE: the backend does NOT accept arbitrary Mongo filters here — only the listed fields. Use get for a specific id. get { id } — GET /v2/skills/:id — single skill (full document) create { payload } — POST /v2/skills — payload keys: label (required), type (required, enum above), plus any of the type-specific fields described in FIELDS. groupId/accountId server-injected. update { id, payload } — PATCH /v2/skills/:id — patch any mutable field. type is NOT accepted on update (backend schema omits it). delete { id } — DELETE /v2/skills/:id — soft-delete (sets status:false) EXAMPLES list { query: { limit: 20, sort: "-createdAt" } } get { id: "<skillId>" } create { payload: { label: "Classify intent", type: "ai", input: "{{message}}", modelId: "<modelId>", configs: {...} } } update { id: "<skillId>", payload: { label: "Classify intent (v2)" } }

smarttalks_submissions

ChatGPT
Submissions — FORM SUBMISSION RECORDS nested under a parent Form. Each submission captures the answers a user provided when they filled out a CRM form. Submissions are read-only from the MCP perspective (create/delete routes are not exposed publicly); they are written by the public form endpoint when a submitter clicks Send. Scoped to one Group + Account; every query is auto-scoped to the caller's JWT claims. INPUT SHAPE { resource, action, formId, ...args } NOTE: formId is REQUIRED on every action. It identifies which form's submissions to query. IDENTITY & TENANCY (applies to every submission record) _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account formId (ObjectId, required) — the Form this submission belongs to createdAt, updatedAt (Date) — managed automatically SUBMISSION CONTENT data (object, free-form) — the actual field answers keyed by form field id (the field.id 12-char nanoid from the Form's steps[].fields[].id). Values are whatever the submitter entered; types vary by field type (string, number, boolean, array, etc.). Examples: { "abc123xyz789": "John Doe" } — short_text answer { "def456uvw012": "john@example.com" } — email answer { "ghi789rst345": ["Option A"] } — multi_select answer { "jkl012mno678": true } — checkbox answer UTM TRACKING (sub-object, optional) utm.source (string) — UTM source parameter captured from the submission URL utm.medium (string) — UTM medium parameter utm.campaign (string) — UTM campaign parameter utm.term (string) — UTM term parameter utm.content (string) — UTM content parameter INDEXES (shape queries to hit these efficiently) formId — primary index for list queries; always provide formId RELATIONSHIPS Group 1 ── N Submission.groupId Account 1 ── N Submission.accountId Form 1 ── N Submission.formId — parent form; fetch form schema via smarttalks_forms.get to interpret data keys Entity Item — if the parent form has entityId set, each submission may create/update an Item in the linked entity (smarttalks_items) RESOURCES & ACTIONS submission: list { formId, skip?, limit?, sortBy?, sortOrder? } Paginated list of submissions for a given form. All pagination params are optional. formId (string, REQUIRED) — the _id of the parent Form skip (number, default 0), limit (number, default 10) sortBy (string, default "createdAt"), sortOrder ("asc" | "desc", default "desc") Response: { items: Submission[], count: number } Tip: call smarttalks_forms.get first to get the form's field definitions so you can map data keys to labels. get { formId, id } Fetch one submission by its _id within a specific form. formId (string, REQUIRED) — the _id of the parent Form id (string, REQUIRED) — the _id of the Submission WRITE ACTIONS — NOT EXPOSED Submissions are created via the public submit endpoint (POST /v1/forms/:formId/submit) and cannot be deleted through this tool. Use smarttalks_forms.delete to remove the parent form if needed. EXAMPLES submission.list { formId: "<formId>", limit: 50, sortOrder: "desc" } submission.list { formId: "<formId>", skip: 10, limit: 10 } submission.get { formId: "<formId>", id: "<submissionId>" }

smarttalks_tags

ChatGPT
Tags, classification LABELS that can be applied to live interactions, ERM records, or user profiles. Each tag declares, via three boolean scope flags, which object types it is allowed to annotate. Visibility can be restricted to specific Units or left open to the whole Account. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier (assigned on first save via Counter model) groupId (ObjectId) — parent tenant Group, inherited from the Account at create time accountId (ObjectId, required) — owning Account; every query is scoped to this value CLASSIFICATION name (string, default "") — human-readable display label shown in the UI color (string, default "") — CSS color or hex code used to render the label badge SCOPE FLAGS — gate which object types this tag may annotate (at least one should be true for the tag to be useful) isLive (boolean, default false) — when true the tag can be applied to live/chat interactions isERM (boolean, default false) — when true the tag can be applied to ERM (contact/history) records isUser (boolean, default false) — when true the tag can be applied to user profiles SCOPE FLAGS (units visibility) units[] (ObjectId[]) — restricts which Units can see and use this tag; empty array (default) means the tag is account-wide and visible to all Units; when the authenticated user belongs to specific units, the read layer filters tags to those whose units[] array contains at least one of the user's units ($in semantics) LIFECYCLE status (boolean, default true) — soft-delete flag; list queries apply { status: true } automatically via a pre-find hook, so deleted tags are never returned by default createdAt (Date, default now) — creation timestamp updatedAt (Date, default now) — last-modified timestamp; updated automatically by a pre-update hook All changes are mirrored to the Audits collection via a MongoDB change-stream watch. RELATIONSHIPS Account 1 ── N Tag.accountId Tag N ── M Units via Tag.units[] (see smarttalks_units); empty units[] means account-wide Tag is referenced by smarttalks_queues: Queue.expiration.queue.tags[] (ObjectId[]) — a queue expiration policy can target a set of tags when routing aged-out interactions (cross-tool ref to smarttalks_queues) ACTIONS get { id } — fetch one tag by _id list { query, project?, options? } — Mongo filter applied on top of the implicit { status: true } scope. Common shapes: { accountId, isLive: true } — all live-interaction tags for an account { accountId, isERM: true } — all ERM tags for an account { accountId, isUser: true } — all user-profile tags for an account { accountId, units: { $in: ["<unitId>"] } } — tags visible to a specific Unit options supports { limit, skip, sort } create { payload } — required: name, accountId; recommended: set at least one of isLive/isERM/isUser; groupId is server-injected from auth context update { id, payload } — mutate name, color, scope flags (isLive/isERM/isUser), or units[]; groupId is IMMUTABLE after create delete { id } — soft-delete (sets status:false via updateOne)

smarttalks_tags_manage

ChatGPT
Tags, classification LABELS that can be applied to live interactions, ERM records, or user profiles. Each tag declares, via three boolean scope flags, which object types it is allowed to annotate. Visibility can be restricted to specific Units or left open to the whole Account. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier (assigned on first save via Counter model) groupId (ObjectId) — parent tenant Group, inherited from the Account at create time accountId (ObjectId, required) — owning Account; every query is scoped to this value CLASSIFICATION name (string, default "") — human-readable display label shown in the UI color (string, default "") — CSS color or hex code used to render the label badge SCOPE FLAGS — gate which object types this tag may annotate (at least one should be true for the tag to be useful) isLive (boolean, default false) — when true the tag can be applied to live/chat interactions isERM (boolean, default false) — when true the tag can be applied to ERM (contact/history) records isUser (boolean, default false) — when true the tag can be applied to user profiles SCOPE FLAGS (units visibility) units[] (ObjectId[]) — restricts which Units can see and use this tag; empty array (default) means the tag is account-wide and visible to all Units; when the authenticated user belongs to specific units, the read layer filters tags to those whose units[] array contains at least one of the user's units ($in semantics) LIFECYCLE status (boolean, default true) — soft-delete flag; list queries apply { status: true } automatically via a pre-find hook, so deleted tags are never returned by default createdAt (Date, default now) — creation timestamp updatedAt (Date, default now) — last-modified timestamp; updated automatically by a pre-update hook All changes are mirrored to the Audits collection via a MongoDB change-stream watch. RELATIONSHIPS Account 1 ── N Tag.accountId Tag N ── M Units via Tag.units[] (see smarttalks_units); empty units[] means account-wide Tag is referenced by smarttalks_queues: Queue.expiration.queue.tags[] (ObjectId[]) — a queue expiration policy can target a set of tags when routing aged-out interactions (cross-tool ref to smarttalks_queues) ACTIONS get { id } — fetch one tag by _id list { query, project?, options? } — Mongo filter applied on top of the implicit { status: true } scope. Common shapes: { accountId, isLive: true } — all live-interaction tags for an account { accountId, isERM: true } — all ERM tags for an account { accountId, isUser: true } — all user-profile tags for an account { accountId, units: { $in: ["<unitId>"] } } — tags visible to a specific Unit options supports { limit, skip, sort } create { payload } — required: name, accountId; recommended: set at least one of isLive/isERM/isUser; groupId is server-injected from auth context update { id, payload } — mutate name, color, scope flags (isLive/isERM/isUser), or units[]; groupId is IMMUTABLE after create delete { id } — soft-delete (sets status:false via updateOne)

smarttalks_tasks

ChatGPT
Tasks — actionable WORK ITEMS in the CRM. A task has a title, an assignee, a due date and a status lifecycle; it can be linked to CRM entity items, commented on, and have files attached. Every task is auto-scoped to the caller's tenant (accountId injected server-side from JWT) and filtered by the caller's task permission scope (own / department / unit / account). INPUT SHAPE { resource, action, ...args } resource is one of: "task" | "comment" | "attachment" | "activity". IDENTITY & TENANCY (task) _id (ObjectId) accountId (ObjectId, server-injected) — parent Account creatorId (ObjectId, server-injected) — set to the caller on create unitId, departmentId (ObjectId, server-resolved) — derived from the assignee's unit/department createdAt, updatedAt (Date) — managed automatically isArchived / isDeleted (boolean) — TWO soft-delete flags (see SOFT-DELETE below) CORE FIELDS (task create/update payload) title (string, required on create, ≤ 280 chars) description (string, default "") status (enum: "pending" | "in_progress" | "on_hold" | "completed" | "cancelled", default "pending") On create/update you may only set pending / in_progress / on_hold. To reach completed or cancelled use the complete / cancel actions; to leave them use reopen. priority (enum: "low" | "normal" | "high" | "urgent", default "normal") dueDate (ISO date string | null) — changing it needs the canChangeDueDate permission isAllDay (boolean, default false) — all-day tasks compare due against start-of-day assigneeId (ObjectId | null) — assigning to others needs canCreateTaskForOthers / canReassignTasks requesterId (ObjectId | null), requesterType (enum: "user" | "agent" | "system" | "automation", default "user") source (object): { type: "manual" | "kanban" | "workflow" | "agent" | "automation" | "live_chat" (default "manual"), refId?, kanbanViewId?, kanbanStageValue?, taskTemplateId? } relations (object[]) — links to CRM entity items. Each: { entityType: "contact" | <Entity ObjectId>, entityId: <Item ObjectId> }. Max ONE relation per entityType; non-contact entities must have allowTaskLink:true. mandatory (boolean, default false) Server-managed (do NOT send): accountId, creatorId, unitId, departmentId, completedAt, completionOnTime, previousStatus, archivedAt/By, deletedAt/By. SOFT-DELETE isArchived — user archive/unarchive; archived tasks are hidden unless list query asks for them. isDeleted — delete action; recoverable only by an admin via the restore action. RESOURCES & ACTIONS task: get { taskId } — GET /v1/tasks/:taskId list { query? } — GET /v1/tasks. query is a bag of filters forwarded as the querystring. Common keys: tab ("today"|"overdue"|"week"|"upcoming"|"completed"|"archived"), status, priority, assigneeId, unitId, departmentId, creatorId, requesterId, entityType, entityId, source, sourceRefId, kanbanViewId, kanbanStageValue, taskTemplateId, mandatory, isAllDay, hasDueDate, search, archived, dueFrom, dueTo, createdFrom, createdTo, completedFrom, completedTo, sort (default "dueDate", prefix "-" for desc), page (default 1), pageSize (1–100, default 20). Response: { data: Task[], pagination: { page, pageSize, total, pages } }. search { q, page?, pageSize? } — GET /v1/tasks/search. Full-text over title, description, comment bodies. metrics { scope?, unitId?, departmentId?, period?, from?, to?, compareWithPrevious? } GET /v1/tasks/metrics. period "personalized" requires from + to. Needs canViewMetrics. entityTypes { } — GET /v1/tasks/relations/entity-types. Entity types linkable from a task. entityItems { entityType, contactId?, search?, limit?, skip? } GET /v1/tasks/relations/entity-items. Items of an entity type, for picking a relation. create { payload } — POST /v1/tasks. Required: title. update { taskId, payload } — PATCH /v1/tasks/:taskId. Partial; status restricted (see CORE FIELDS). delete { taskId } — DELETE /v1/tasks/:taskId. Soft-delete (isDeleted). complete { taskId } — POST /v1/tasks/:taskId/complete. Sets completedAt + computes completionOnTime. reopen { taskId } — POST /v1/tasks/:taskId/reopen. Restores a completed/cancelled task to its previous status. cancel { taskId } — POST /v1/tasks/:taskId/cancel. archive { taskId } — POST /v1/tasks/:taskId/archive. unarchive { taskId } — POST /v1/tasks/:taskId/unarchive. restore { taskId } — POST /v1/tasks/:taskId/restore. Admin only; reverses delete. assign { taskId, payload } — POST /v1/tasks/:taskId/assign. payload: { assigneeId: <userId> | null }. bulkPatch { payload } — PATCH /v1/tasks/bulk. payload: { ids: string[] (≤ 500), patch: { status?, priority?, dueDate?, assigneeId?, isArchived? } }. Response: { updated: Task[], failed: { id, code, message }[] }. bulkComplete { payload } — POST /v1/tasks/bulk/complete. payload: { ids: string[] (≤ 500) }. bulkArchive { payload } — POST /v1/tasks/bulk/archive. payload: { ids: string[] (≤ 500) }. bulkDelete { payload } — DELETE /v1/tasks/bulk. payload: { ids: string[] (≤ 500) }. bulkReassign { payload } — POST /v1/tasks/bulk/reassign. payload: { ids: string[] (≤ 500), assigneeId: <userId> | null }. comment: list { taskId } — GET /v1/tasks/:taskId/comments. Sorted oldest-first. create { taskId, payload } — POST /v1/tasks/:taskId/comments. payload: { body: <HTML string> }. Mentions: include data-mention-id="<userId>" attributes in body. delete { taskId, commentId } — DELETE /v1/tasks/:taskId/comments/:commentId. attachment: list { taskId } — GET /v1/tasks/:taskId/attachments. Sorted newest-first. create { taskId, payload } — POST /v1/tasks/:taskId/attachments. payload: { voyagerFileId (string, required), name (string, required), url (string, required), mimeType? (string), size? (number, bytes) }. delete { taskId, attachmentId } — DELETE /v1/tasks/:taskId/attachments/:attachmentId. activity: list { taskId } — GET /v1/tasks/:taskId/activity. Append-only audit log, newest-first. Events: created, updated, status_changed, reassigned, due_changed, archived, unarchived, deleted, restored, reopened, commented, attached. EXAMPLES task.list { query: { tab: "overdue", priority: "high", pageSize: 50 } } task.get { taskId: "<tid>" } task.create { payload: { title: "Call lead", dueDate: "2026-05-30", priority: "high", assigneeId: "<uid>" } } task.update { taskId: "<tid>", payload: { status: "in_progress" } } task.complete { taskId: "<tid>" } task.assign { taskId: "<tid>", payload: { assigneeId: "<uid>" } } task.bulkPatch { payload: { ids: ["<t1>", "<t2>"], patch: { priority: "urgent" } } } task.search { q: "invoice", page: 1, pageSize: 20 } task.metrics { period: "lastThirtyDays", scope: "account", compareWithPrevious: true } task.entityItems { entityType: "<entityId>", search: "Acme", limit: 20 } comment.create { taskId: "<tid>", payload: { body: "<p>Done <span data-mention-id=\"<uid>\">@user</span></p>" } } attachment.create { taskId: "<tid>", payload: { voyagerFileId: "<fid>", name: "quote.pdf", url: "https://..." } } activity.list { taskId: "<tid>" } NOT EXPOSED POST /v1/tasks/auto-archive — internal Cronos callback, gated by an x-internal-secret header; not callable with a user JWT.

smarttalks_tasks_manage

ChatGPT
Tasks — actionable WORK ITEMS in the CRM. A task has a title, an assignee, a due date and a status lifecycle; it can be linked to CRM entity items, commented on, and have files attached. Every task is auto-scoped to the caller's tenant (accountId injected server-side from JWT) and filtered by the caller's task permission scope (own / department / unit / account). INPUT SHAPE { resource, action, ...args } resource is one of: "task" | "comment" | "attachment" | "activity". IDENTITY & TENANCY (task) _id (ObjectId) accountId (ObjectId, server-injected) — parent Account creatorId (ObjectId, server-injected) — set to the caller on create unitId, departmentId (ObjectId, server-resolved) — derived from the assignee's unit/department createdAt, updatedAt (Date) — managed automatically isArchived / isDeleted (boolean) — TWO soft-delete flags (see SOFT-DELETE below) CORE FIELDS (task create/update payload) title (string, required on create, ≤ 280 chars) description (string, default "") status (enum: "pending" | "in_progress" | "on_hold" | "completed" | "cancelled", default "pending") On create/update you may only set pending / in_progress / on_hold. To reach completed or cancelled use the complete / cancel actions; to leave them use reopen. priority (enum: "low" | "normal" | "high" | "urgent", default "normal") dueDate (ISO date string | null) — changing it needs the canChangeDueDate permission isAllDay (boolean, default false) — all-day tasks compare due against start-of-day assigneeId (ObjectId | null) — assigning to others needs canCreateTaskForOthers / canReassignTasks requesterId (ObjectId | null), requesterType (enum: "user" | "agent" | "system" | "automation", default "user") source (object): { type: "manual" | "kanban" | "workflow" | "agent" | "automation" | "live_chat" (default "manual"), refId?, kanbanViewId?, kanbanStageValue?, taskTemplateId? } relations (object[]) — links to CRM entity items. Each: { entityType: "contact" | <Entity ObjectId>, entityId: <Item ObjectId> }. Max ONE relation per entityType; non-contact entities must have allowTaskLink:true. mandatory (boolean, default false) Server-managed (do NOT send): accountId, creatorId, unitId, departmentId, completedAt, completionOnTime, previousStatus, archivedAt/By, deletedAt/By. SOFT-DELETE isArchived — user archive/unarchive; archived tasks are hidden unless list query asks for them. isDeleted — delete action; recoverable only by an admin via the restore action. RESOURCES & ACTIONS task: get { taskId } — GET /v1/tasks/:taskId list { query? } — GET /v1/tasks. query is a bag of filters forwarded as the querystring. Common keys: tab ("today"|"overdue"|"week"|"upcoming"|"completed"|"archived"), status, priority, assigneeId, unitId, departmentId, creatorId, requesterId, entityType, entityId, source, sourceRefId, kanbanViewId, kanbanStageValue, taskTemplateId, mandatory, isAllDay, hasDueDate, search, archived, dueFrom, dueTo, createdFrom, createdTo, completedFrom, completedTo, sort (default "dueDate", prefix "-" for desc), page (default 1), pageSize (1–100, default 20). Response: { data: Task[], pagination: { page, pageSize, total, pages } }. search { q, page?, pageSize? } — GET /v1/tasks/search. Full-text over title, description, comment bodies. metrics { scope?, unitId?, departmentId?, period?, from?, to?, compareWithPrevious? } GET /v1/tasks/metrics. period "personalized" requires from + to. Needs canViewMetrics. entityTypes { } — GET /v1/tasks/relations/entity-types. Entity types linkable from a task. entityItems { entityType, contactId?, search?, limit?, skip? } GET /v1/tasks/relations/entity-items. Items of an entity type, for picking a relation. create { payload } — POST /v1/tasks. Required: title. update { taskId, payload } — PATCH /v1/tasks/:taskId. Partial; status restricted (see CORE FIELDS). delete { taskId } — DELETE /v1/tasks/:taskId. Soft-delete (isDeleted). complete { taskId } — POST /v1/tasks/:taskId/complete. Sets completedAt + computes completionOnTime. reopen { taskId } — POST /v1/tasks/:taskId/reopen. Restores a completed/cancelled task to its previous status. cancel { taskId } — POST /v1/tasks/:taskId/cancel. archive { taskId } — POST /v1/tasks/:taskId/archive. unarchive { taskId } — POST /v1/tasks/:taskId/unarchive. restore { taskId } — POST /v1/tasks/:taskId/restore. Admin only; reverses delete. assign { taskId, payload } — POST /v1/tasks/:taskId/assign. payload: { assigneeId: <userId> | null }. bulkPatch { payload } — PATCH /v1/tasks/bulk. payload: { ids: string[] (≤ 500), patch: { status?, priority?, dueDate?, assigneeId?, isArchived? } }. Response: { updated: Task[], failed: { id, code, message }[] }. bulkComplete { payload } — POST /v1/tasks/bulk/complete. payload: { ids: string[] (≤ 500) }. bulkArchive { payload } — POST /v1/tasks/bulk/archive. payload: { ids: string[] (≤ 500) }. bulkDelete { payload } — DELETE /v1/tasks/bulk. payload: { ids: string[] (≤ 500) }. bulkReassign { payload } — POST /v1/tasks/bulk/reassign. payload: { ids: string[] (≤ 500), assigneeId: <userId> | null }. comment: list { taskId } — GET /v1/tasks/:taskId/comments. Sorted oldest-first. create { taskId, payload } — POST /v1/tasks/:taskId/comments. payload: { body: <HTML string> }. Mentions: include data-mention-id="<userId>" attributes in body. delete { taskId, commentId } — DELETE /v1/tasks/:taskId/comments/:commentId. attachment: list { taskId } — GET /v1/tasks/:taskId/attachments. Sorted newest-first. create { taskId, payload } — POST /v1/tasks/:taskId/attachments. payload: { voyagerFileId (string, required), name (string, required), url (string, required), mimeType? (string), size? (number, bytes) }. delete { taskId, attachmentId } — DELETE /v1/tasks/:taskId/attachments/:attachmentId. activity: list { taskId } — GET /v1/tasks/:taskId/activity. Append-only audit log, newest-first. Events: created, updated, status_changed, reassigned, due_changed, archived, unarchived, deleted, restored, reopened, commented, attached. EXAMPLES task.list { query: { tab: "overdue", priority: "high", pageSize: 50 } } task.get { taskId: "<tid>" } task.create { payload: { title: "Call lead", dueDate: "2026-05-30", priority: "high", assigneeId: "<uid>" } } task.update { taskId: "<tid>", payload: { status: "in_progress" } } task.complete { taskId: "<tid>" } task.assign { taskId: "<tid>", payload: { assigneeId: "<uid>" } } task.bulkPatch { payload: { ids: ["<t1>", "<t2>"], patch: { priority: "urgent" } } } task.search { q: "invoice", page: 1, pageSize: 20 } task.metrics { period: "lastThirtyDays", scope: "account", compareWithPrevious: true } task.entityItems { entityType: "<entityId>", search: "Acme", limit: 20 } comment.create { taskId: "<tid>", payload: { body: "<p>Done <span data-mention-id=\"<uid>\">@user</span></p>" } } attachment.create { taskId: "<tid>", payload: { voyagerFileId: "<fid>", name: "quote.pdf", url: "https://..." } } activity.list { taskId: "<tid>" } NOT EXPOSED POST /v1/tasks/auto-archive — internal Cronos callback, gated by an x-internal-secret header; not callable with a user JWT.

smarttalks_templates

ChatGPT
Templates — WHATSAPP HSM (Highly Structured Message) templates managed via the Dialog360 / Gupshup providers. A Template is a pre-approved message blueprint composed of typed components (HEADER / BODY / FOOTER / BUTTONS) that operators reuse for outbound campaigns and live-active sends. Scoped to ONE Group + Account; soft-deleted via status. Approval status is owned by the WhatsApp provider, not SmartTalks. INPUT SHAPE { action, ...args } IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment) groupId (ObjectId, required, server-injected) — IMMUTABLE accountId (ObjectId, required, server-injected) — IMMUTABLE status (boolean, default true) — soft-delete; pre-find middleware filters status:true createdAt, updatedAt (Date) — server-managed; pre-update hook bumps updatedAt createdBy, updatedBy, deletedBy (ObjectId — Users) CORE FIELDS name (string, REQUIRED) — display name supplied by the caller. On create the model derives templateName from this by lowercasing, stripping non-word chars, and converting spaces to underscores (the actual provider element name). templateName (string) — provider-side element name. Server-derived from name at create time; do NOT set on create. language (string, REQUIRED) — BCP-47-ish locale tag accepted by the provider, e.g. "pt_BR" | "en_US" | "es_ES" | "pt_PT" | "fr". category (enum, REQUIRED): "MARKETING" | "UTILITY" | "AUTHENTICATION". oldCategory (string) — previous category if it was changed post-approval. channel (enum, default "whatsapp"): "whatsapp" | "telegram" | "instagram" | "sms" | "email" — practically always "whatsapp" for HSM templates today. channelId (ObjectId, REQUIRED on create) — the SmartTalks Channel this template belongs to. Channel type drives which provider (dialog360 / gupshup) handles registration. namespace (string) — provider-assigned namespace. Server-injected from the provider response on create; do NOT set. policy (string, default "deterministic"). body (string) — convenience rendered body text; the source of truth is the BODY component's text. hasVariables (boolean, default false) — whether ANY component carries {{n}} placeholders. numberOfVariables (number) — total placeholder count across all components. PROVIDER LINKAGE gupshupId (string) — provider id for Gupshup channels; server-injected. gsHandleId / handleId (string) — media handle ids used by some Gupshup template flows. statusTemplate (enum, default "PENDING"): "APPROVED" | "IN_APPEAL" | "PENDING" | "REJECTED" | "PENDING_DELETION" | "DELETED" | "DISABLED" | "PAUSED". OWNED by the provider; only APPROVED templates can actually be sent. Webhooks from Dialog360 / Gupshup flip this server-side. rejectedReason (string) — populated when statusTemplate === "REJECTED". VISIBILITY / SCOPE units[] (ObjectId[]) — Units allowed to use this template. Empty / missing means "all units". The read endpoint applies a UNITS-FILTER: callers whose JWT carries units[] see templates whose units intersects, plus templates with no units; callers without units see everything. departments[] (ObjectId[]) — Departments tagged on the template. Display/grouping only at present. COMPONENTS (array, ordered) Each entry: { type, format?, text?, example?, hasVariables?, buttons?, add_security_recommendation?, code_expiration_minutes? } type (enum, required): "BODY" | "HEADER" | "FOOTER" | "BUTTONS" format (enum, HEADER only): "TEXT" | "IMAGE" | "DOCUMENT" | "VIDEO" text (string): HEADER/BODY/FOOTER text content. May contain {{n}} variable placeholders. example (mixed): provider-required sample values for variables and media handles (e.g. { header_handle: ["https://…"] } for IMAGE/VIDEO/DOCUMENT headers). hasVariables (boolean, default false): server-computed on create from the text content; do NOT set. add_security_recommendation (boolean, default true): AUTHENTICATION BODY only — adds the standard "do not share" line. code_expiration_minutes (number, default 10): AUTHENTICATION FOOTER only — code lifetime. buttons[]: required when type === "BUTTONS"; each entry: { type, text?, url?, phone_number?, example?, hasVariable? } type (enum): "PHONE_NUMBER" | "URL" | "QUICK_REPLY" | "OTP" text: button label url: required when type === "URL". Dynamic URLs use a trailing {{1}}. phone_number: required when type === "PHONE_NUMBER". Digits only, max 20 chars (no leading +). example: provider-required sample value (used for URL dynamic variables / OTP copy text). hasVariable (boolean, default false): server-computed; do NOT set. Standard layout: [BODY] (always), optional HEADER, optional FOOTER, optional BUTTONS. AUTHENTICATION templates use a fixed shape: BODY { type: "BODY", add_security_recommendation: true }, FOOTER { type: "FOOTER", code_expiration_minutes }, BUTTONS [{ type: "OTP", otp_type: "COPY_CODE" }]. PROVIDER SIDE-EFFECTS create — calls Dialog360.createTemplate or Gupshup.create with the components. On success, namespace (and gupshupId for Gupshup) are written back; on failure the row is NOT created and the request fails with the provider error. The duplicate-name check (templateName uniqueness within {groupId, accountId, channelId}) runs BEFORE the provider call. update — local-only by default. Does NOT push changes to the provider; for content edits you generally need to delete + recreate. delete — calls Dialog360.deleteTemplate or Gupshup.delete FIRST; only if the provider succeeds is the row soft-deleted (status:false). This avoids "ghost" templates that disappear in the UI but remain at the provider. QUIRKS (READ CAREFULLY BEFORE WIRING UI / TESTS) • update wraps differently upstream: the PATCH route spreads req.body directly into $set instead of unwrapping {payload}. The MCP handler accommodates this by sending the payload as the request body (no {payload} envelope). Inside MCP you still pass { action: "update", id, payload } — the handler reshapes it. • Name normalisation on create: pass a human-readable name; the model writes both the human name and the derived templateName. • hasVariables / numberOfVariables / hasVariable (per button) are server-computed — do NOT pass them. LIST FILTERING & PAGINATION list — POST /v1/templates/dialog360 with { query, project, options }. Server appends tenant scope, the units filter (see VISIBILITY), and the count. Response shape: { count, items[] }, sorted by { createdAt: -1 }. options accepts { skip, limit } (defaults 0 / 10). RELATIONSHIPS Group 1 ── N Template.groupId Account 1 ── N Template.accountId Channel 1 ── N Template.channelId (REQUIRED — drives which provider handles registration) Unit N ── M via Template.units[] Department N ── M via Template.departments[] Campaign N ── 1 Template via Campaign.templateId (HSM campaigns reference an approved template) ACTIONS list { query?, project?, options? } POST /v1/templates/dialog360. Common queries: { statusTemplate: "APPROVED" } — sendable templates only { channelId: "<id>" } — templates on one channel { category: "MARKETING" } — by category { language: "pt_BR" } — by language { name: { $regex: "boas-vindas", $options: "i" } } — substring search options: { skip, limit } (defaults 0 / 10). get { id } GET /v1/templates/dialog360/:id — one template, tenant + units scoped. create { payload } PUT /v1/templates/dialog360. Required in payload: name, language, category, channelId, components[] (at least a BODY). Optional: units[], departments[], handleId (Gupshup), per-component example for variable samples. REJECTED ON INPUT (server-managed; the validator returns invalid_payload if any are set): groupId, accountId, createdBy, updatedBy, deletedBy, createdAt, updatedAt, _id, id, templateName, namespace, gupshupId, gsHandleId, statusTemplate, rejectedReason, hasVariables, numberOfVariables, status, policy, channel, oldCategory, body. IMPORTANT: calls the provider (Dialog360 / Gupshup) synchronously. A provider error fails the whole call and no row is persisted. update { id, payload } PATCH /v1/templates/dialog360/:id. Patches mutable local fields only (does NOT re-push to the provider). updatedBy/updatedAt are server-stamped. Do NOT pass groupId/accountId/createdBy/createdAt/_id, or provider-owned fields (templateName, namespace, gupshupId, statusTemplate, rejectedReason). delete { id } DELETE /v1/templates/dialog360/:id — soft-delete. The provider delete runs FIRST; if the provider rejects (e.g. template in use, network failure), the local row stays alive. NOT EXPOSED • POST /v1/templates/sync/:channelId — batch-sync templates from the provider. Use the upstream API directly for now; will be added as an action when the response shape stabilises. • POST /v1/templates/gupshup/media — Gupshup partner-token / media-handle proxy used by the create form. Out of scope for MCP today. • GET/POST /v1/templates/:_id? — legacy fallback routes that read templates without the /dialog360 prefix. Prefer the /dialog360 routes wired here. EXAMPLES list { query: { statusTemplate: "APPROVED", category: "MARKETING" }, options: { limit: 20 } } get { id: "<templateId>" } create { payload: { name: "Boas vindas", language: "pt_BR", category: "UTILITY", channelId: "<channelId>", components: [ { type: "BODY", text: "Olá {{1}}, bem-vindo!" } ] } } update { id: "<templateId>", payload: { departments: ["<deptId>"] } } delete { id: "<templateId>" }

smarttalks_templates_manage

ChatGPT
Templates — WHATSAPP HSM (Highly Structured Message) templates managed via the Dialog360 / Gupshup providers. A Template is a pre-approved message blueprint composed of typed components (HEADER / BODY / FOOTER / BUTTONS) that operators reuse for outbound campaigns and live-active sends. Scoped to ONE Group + Account; soft-deleted via status. Approval status is owned by the WhatsApp provider, not SmartTalks. INPUT SHAPE { action, ...args } IDENTITY & TENANCY _id (ObjectId), id (number — auto-increment) groupId (ObjectId, required, server-injected) — IMMUTABLE accountId (ObjectId, required, server-injected) — IMMUTABLE status (boolean, default true) — soft-delete; pre-find middleware filters status:true createdAt, updatedAt (Date) — server-managed; pre-update hook bumps updatedAt createdBy, updatedBy, deletedBy (ObjectId — Users) CORE FIELDS name (string, REQUIRED) — display name supplied by the caller. On create the model derives templateName from this by lowercasing, stripping non-word chars, and converting spaces to underscores (the actual provider element name). templateName (string) — provider-side element name. Server-derived from name at create time; do NOT set on create. language (string, REQUIRED) — BCP-47-ish locale tag accepted by the provider, e.g. "pt_BR" | "en_US" | "es_ES" | "pt_PT" | "fr". category (enum, REQUIRED): "MARKETING" | "UTILITY" | "AUTHENTICATION". oldCategory (string) — previous category if it was changed post-approval. channel (enum, default "whatsapp"): "whatsapp" | "telegram" | "instagram" | "sms" | "email" — practically always "whatsapp" for HSM templates today. channelId (ObjectId, REQUIRED on create) — the SmartTalks Channel this template belongs to. Channel type drives which provider (dialog360 / gupshup) handles registration. namespace (string) — provider-assigned namespace. Server-injected from the provider response on create; do NOT set. policy (string, default "deterministic"). body (string) — convenience rendered body text; the source of truth is the BODY component's text. hasVariables (boolean, default false) — whether ANY component carries {{n}} placeholders. numberOfVariables (number) — total placeholder count across all components. PROVIDER LINKAGE gupshupId (string) — provider id for Gupshup channels; server-injected. gsHandleId / handleId (string) — media handle ids used by some Gupshup template flows. statusTemplate (enum, default "PENDING"): "APPROVED" | "IN_APPEAL" | "PENDING" | "REJECTED" | "PENDING_DELETION" | "DELETED" | "DISABLED" | "PAUSED". OWNED by the provider; only APPROVED templates can actually be sent. Webhooks from Dialog360 / Gupshup flip this server-side. rejectedReason (string) — populated when statusTemplate === "REJECTED". VISIBILITY / SCOPE units[] (ObjectId[]) — Units allowed to use this template. Empty / missing means "all units". The read endpoint applies a UNITS-FILTER: callers whose JWT carries units[] see templates whose units intersects, plus templates with no units; callers without units see everything. departments[] (ObjectId[]) — Departments tagged on the template. Display/grouping only at present. COMPONENTS (array, ordered) Each entry: { type, format?, text?, example?, hasVariables?, buttons?, add_security_recommendation?, code_expiration_minutes? } type (enum, required): "BODY" | "HEADER" | "FOOTER" | "BUTTONS" format (enum, HEADER only): "TEXT" | "IMAGE" | "DOCUMENT" | "VIDEO" text (string): HEADER/BODY/FOOTER text content. May contain {{n}} variable placeholders. example (mixed): provider-required sample values for variables and media handles (e.g. { header_handle: ["https://…"] } for IMAGE/VIDEO/DOCUMENT headers). hasVariables (boolean, default false): server-computed on create from the text content; do NOT set. add_security_recommendation (boolean, default true): AUTHENTICATION BODY only — adds the standard "do not share" line. code_expiration_minutes (number, default 10): AUTHENTICATION FOOTER only — code lifetime. buttons[]: required when type === "BUTTONS"; each entry: { type, text?, url?, phone_number?, example?, hasVariable? } type (enum): "PHONE_NUMBER" | "URL" | "QUICK_REPLY" | "OTP" text: button label url: required when type === "URL". Dynamic URLs use a trailing {{1}}. phone_number: required when type === "PHONE_NUMBER". Digits only, max 20 chars (no leading +). example: provider-required sample value (used for URL dynamic variables / OTP copy text). hasVariable (boolean, default false): server-computed; do NOT set. Standard layout: [BODY] (always), optional HEADER, optional FOOTER, optional BUTTONS. AUTHENTICATION templates use a fixed shape: BODY { type: "BODY", add_security_recommendation: true }, FOOTER { type: "FOOTER", code_expiration_minutes }, BUTTONS [{ type: "OTP", otp_type: "COPY_CODE" }]. PROVIDER SIDE-EFFECTS create — calls Dialog360.createTemplate or Gupshup.create with the components. On success, namespace (and gupshupId for Gupshup) are written back; on failure the row is NOT created and the request fails with the provider error. The duplicate-name check (templateName uniqueness within {groupId, accountId, channelId}) runs BEFORE the provider call. update — local-only by default. Does NOT push changes to the provider; for content edits you generally need to delete + recreate. delete — calls Dialog360.deleteTemplate or Gupshup.delete FIRST; only if the provider succeeds is the row soft-deleted (status:false). This avoids "ghost" templates that disappear in the UI but remain at the provider. QUIRKS (READ CAREFULLY BEFORE WIRING UI / TESTS) • update wraps differently upstream: the PATCH route spreads req.body directly into $set instead of unwrapping {payload}. The MCP handler accommodates this by sending the payload as the request body (no {payload} envelope). Inside MCP you still pass { action: "update", id, payload } — the handler reshapes it. • Name normalisation on create: pass a human-readable name; the model writes both the human name and the derived templateName. • hasVariables / numberOfVariables / hasVariable (per button) are server-computed — do NOT pass them. LIST FILTERING & PAGINATION list — POST /v1/templates/dialog360 with { query, project, options }. Server appends tenant scope, the units filter (see VISIBILITY), and the count. Response shape: { count, items[] }, sorted by { createdAt: -1 }. options accepts { skip, limit } (defaults 0 / 10). RELATIONSHIPS Group 1 ── N Template.groupId Account 1 ── N Template.accountId Channel 1 ── N Template.channelId (REQUIRED — drives which provider handles registration) Unit N ── M via Template.units[] Department N ── M via Template.departments[] Campaign N ── 1 Template via Campaign.templateId (HSM campaigns reference an approved template) ACTIONS list { query?, project?, options? } POST /v1/templates/dialog360. Common queries: { statusTemplate: "APPROVED" } — sendable templates only { channelId: "<id>" } — templates on one channel { category: "MARKETING" } — by category { language: "pt_BR" } — by language { name: { $regex: "boas-vindas", $options: "i" } } — substring search options: { skip, limit } (defaults 0 / 10). get { id } GET /v1/templates/dialog360/:id — one template, tenant + units scoped. create { payload } PUT /v1/templates/dialog360. Required in payload: name, language, category, channelId, components[] (at least a BODY). Optional: units[], departments[], handleId (Gupshup), per-component example for variable samples. REJECTED ON INPUT (server-managed; the validator returns invalid_payload if any are set): groupId, accountId, createdBy, updatedBy, deletedBy, createdAt, updatedAt, _id, id, templateName, namespace, gupshupId, gsHandleId, statusTemplate, rejectedReason, hasVariables, numberOfVariables, status, policy, channel, oldCategory, body. IMPORTANT: calls the provider (Dialog360 / Gupshup) synchronously. A provider error fails the whole call and no row is persisted. update { id, payload } PATCH /v1/templates/dialog360/:id. Patches mutable local fields only (does NOT re-push to the provider). updatedBy/updatedAt are server-stamped. Do NOT pass groupId/accountId/createdBy/createdAt/_id, or provider-owned fields (templateName, namespace, gupshupId, statusTemplate, rejectedReason). delete { id } DELETE /v1/templates/dialog360/:id — soft-delete. The provider delete runs FIRST; if the provider rejects (e.g. template in use, network failure), the local row stays alive. NOT EXPOSED • POST /v1/templates/sync/:channelId — batch-sync templates from the provider. Use the upstream API directly for now; will be added as an action when the response shape stabilises. • POST /v1/templates/gupshup/media — Gupshup partner-token / media-handle proxy used by the create form. Out of scope for MCP today. • GET/POST /v1/templates/:_id? — legacy fallback routes that read templates without the /dialog360 prefix. Prefer the /dialog360 routes wired here. EXAMPLES list { query: { statusTemplate: "APPROVED", category: "MARKETING" }, options: { limit: 20 } } get { id: "<templateId>" } create { payload: { name: "Boas vindas", language: "pt_BR", category: "UTILITY", channelId: "<channelId>", components: [ { type: "BODY", text: "Olá {{1}}, bem-vindo!" } ] } } update { id: "<templateId>", payload: { departments: ["<deptId>"] } } delete { id: "<templateId>" }

smarttalks_tokens

ChatGPT
Tokens, JWTs issued to individual users for programmatic access. A token is a signed JWT that carries the owner's identity and a subset of permissions, intended for machine-to-machine or scripted access without sharing a password. IDENTITY _id (ObjectId) — internal MongoDB document id id (number) — auto-incremented public identifier (set by Counter plugin on save) TENANCY (SERVER-INJECTED — do NOT pass on create; sourced from the caller's JWT) groupId (ObjectId) — parent tenant Group; copied from auth.groupId at create time accountId (ObjectId, required) — Account scope; copied from auth.accountId at create time userId (ObjectId, required) — owner; copied from auth._id at create time TOKEN PROPERTIES label (string, required) — human-readable name for the token caption (string, optional) — short description token (string) — the issued JWT * SECRET: NEVER log, display, or share this value * The JWT payload contains: groupId, accountId, _id (userId), permissionsGroups, isToken:true, label The JWT is signed with process.env.JWT_SECRET and expires after the requested expiration period. expiration (string, required on create) — lifetime passed directly to jwt.sign as expiresIn. Accepted formats: relative (e.g. "7d", "30d", "1h", "60s") or an absolute ISO-8601 date string. Common values: "7d" (one week), "30d" (one month), "365d" (one year). permissionsGroups[] (ObjectId[]) — role bundles embedded in the JWT and persisted here; see smarttalks_permissions_groups LIFECYCLE status (boolean, default true) — soft-delete flag; list queries automatically filter to status:true createdAt (Date, default Date.now) — creation timestamp updatedAt (Date, default Date.now; auto-updated on any update operation) RELATIONSHIPS User 1 ── N Token — Token.userId references smarttalks_users._id; list a user's tokens via list { query: { userId } } Account 1 ── N Token — Token.accountId references the owning Account Token N ── M PermissionsGroups via permissionsGroups[] — each id references smarttalks_permissions_groups ACTIONS get { id } — fetch one token by _id; returns the full document including the token JWT list { query, project?, options? } — returns { count, items[] }; default options.limit = 10, sort by createdAt desc Common query shapes: { userId: "<userId>" } — all tokens belonging to a specific user { accountId: "<accountId>" } — all tokens in an account { label: "<label>" } — exact or regex match on label options supports { limit, skip, sort } create { payload } — required: label, expiration — optional: caption, permissionsGroups[] — SERVER-INJECTED (do NOT pass): groupId, accountId, userId, token The server signs the JWT using payload.expiration and payload.permissionsGroups, then persists the resulting token string alongside the document. update { id, payload } — mutate any of: label, caption, permissionsGroups[], expiration — NOTE: updating expiration or permissionsGroups does NOT re-sign the JWT; the stored token string remains the original one issued at create time delete { id } — soft-delete: sets status:false; the token document is retained but hidden from list queries

smarttalks_tokens_manage

ChatGPT
Tokens, JWTs issued to individual users for programmatic access. A token is a signed JWT that carries the owner's identity and a subset of permissions, intended for machine-to-machine or scripted access without sharing a password. IDENTITY _id (ObjectId) — internal MongoDB document id id (number) — auto-incremented public identifier (set by Counter plugin on save) TENANCY (SERVER-INJECTED — do NOT pass on create; sourced from the caller's JWT) groupId (ObjectId) — parent tenant Group; copied from auth.groupId at create time accountId (ObjectId, required) — Account scope; copied from auth.accountId at create time userId (ObjectId, required) — owner; copied from auth._id at create time TOKEN PROPERTIES label (string, required) — human-readable name for the token caption (string, optional) — short description token (string) — the issued JWT * SECRET: NEVER log, display, or share this value * The JWT payload contains: groupId, accountId, _id (userId), permissionsGroups, isToken:true, label The JWT is signed with process.env.JWT_SECRET and expires after the requested expiration period. expiration (string, required on create) — lifetime passed directly to jwt.sign as expiresIn. Accepted formats: relative (e.g. "7d", "30d", "1h", "60s") or an absolute ISO-8601 date string. Common values: "7d" (one week), "30d" (one month), "365d" (one year). permissionsGroups[] (ObjectId[]) — role bundles embedded in the JWT and persisted here; see smarttalks_permissions_groups LIFECYCLE status (boolean, default true) — soft-delete flag; list queries automatically filter to status:true createdAt (Date, default Date.now) — creation timestamp updatedAt (Date, default Date.now; auto-updated on any update operation) RELATIONSHIPS User 1 ── N Token — Token.userId references smarttalks_users._id; list a user's tokens via list { query: { userId } } Account 1 ── N Token — Token.accountId references the owning Account Token N ── M PermissionsGroups via permissionsGroups[] — each id references smarttalks_permissions_groups ACTIONS get { id } — fetch one token by _id; returns the full document including the token JWT list { query, project?, options? } — returns { count, items[] }; default options.limit = 10, sort by createdAt desc Common query shapes: { userId: "<userId>" } — all tokens belonging to a specific user { accountId: "<accountId>" } — all tokens in an account { label: "<label>" } — exact or regex match on label options supports { limit, skip, sort } create { payload } — required: label, expiration — optional: caption, permissionsGroups[] — SERVER-INJECTED (do NOT pass): groupId, accountId, userId, token The server signs the JWT using payload.expiration and payload.permissionsGroups, then persists the resulting token string alongside the document. update { id, payload } — mutate any of: label, caption, permissionsGroups[], expiration — NOTE: updating expiration or permissionsGroups does NOT re-sign the JWT; the stored token string remains the original one issued at create time delete { id } — soft-delete: sets status:false; the token document is retained but hidden from list queries

smarttalks_units

ChatGPT
Units, PHYSICAL OR LOGICAL LOCATIONS (branches, offices, departments) within an Account. Units scope users, tags, queue routing, and operational reports. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group; set on create from auth context, immutable thereafter accountId (ObjectId, required, server-injected) — owning Account; set on create from auth context label (string, required) — display name of the unit description (string, default "") — free-text description / notes LOCATION address (string, default "") — street address city (string, default "") — city name; commonly used as a filter key state (string, default null) — state / province country (string, default null) — country cnpj (string, default "") — Brazilian tax-registration number (CNPJ) link (string) — URL to an external resource or map link for this unit ruleInfo (string) — free-text operational rule or policy note attached to this unit CONTACT INFO (sub-object, default null) contactInfo.email (string, default null) — contact e-mail for this unit contactInfo.phone (string, default null) — contact phone number for this unit The entire contactInfo object defaults to null; set it as an object to activate contact fields. OPERATIONS timezone (string, default "Etc/GMT+3") — IANA/Etc timezone used to interpret schedules and reports workSchedule[] (Array, default []) — array of schedule objects describing shift windows; each element represents one shift period (e.g. day-of-week + start/end time). Shape is flexible — pass objects that match your system's scheduling conventions. dateOfActivation (Date, default null) — when this unit became (or will become) operational MEDIA avatar (string, default "") — URL of the unit's avatar / logo image AUDIT createdBy (ObjectId, ref Users) — user who created the record updatedBy (ObjectId, ref Users) — user who last updated the record deletedBy (ObjectId, ref Users) — user who soft-deleted the record LIFECYCLE status (boolean, default true) — soft-delete flag; list queries automatically filter to status:true via a pre-find hook, so deleted units are hidden by default createdAt (Date, default now), updatedAt (Date, auto-updated on any update operation) Every change is captured in the Audits collection via a change-stream watch. RELATIONSHIPS Account 1 ── N Unit — Unit.accountId links to the owning Account Group 1 ── N Unit — Unit.groupId links to the parent Group Users reference units via User.units[] (see smarttalks_users) — a user may belong to many units Tags reference units via Tag.units[] (see smarttalks_tags) — a tag can be restricted to specific units Queues may redirect to units on expiration via expiration.queue.unitId / expiration.queue.units[] (see smarttalks_queues) ACTIONS get { id } — fetch one unit by _id list { query, project?, options? } — Mongo filter. Common shapes: { accountId: "<id>" } — all units for an account { city: "São Paulo" } — units in a city { status: true } — active units only (default behaviour of the pre-find hook) { dateOfActivation: { $lte: new Date() } } — units that have already activated options supports { limit, skip } (default limit 10) create { payload } — required: label; groupId/accountId are server-injected from auth context optional: description, address, city, state, country, cnpj, link, ruleInfo, timezone, workSchedule, avatar, dateOfActivation, contactInfo update { id, payload } — mutate any mutable field; updatedBy is set server-side from auth context delete { id } — soft-delete: sets status:false and records deletedBy

smarttalks_units_manage

ChatGPT
Units, PHYSICAL OR LOGICAL LOCATIONS (branches, offices, departments) within an Account. Units scope users, tags, queue routing, and operational reports. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, required, server-injected) — parent tenant Group; set on create from auth context, immutable thereafter accountId (ObjectId, required, server-injected) — owning Account; set on create from auth context label (string, required) — display name of the unit description (string, default "") — free-text description / notes LOCATION address (string, default "") — street address city (string, default "") — city name; commonly used as a filter key state (string, default null) — state / province country (string, default null) — country cnpj (string, default "") — Brazilian tax-registration number (CNPJ) link (string) — URL to an external resource or map link for this unit ruleInfo (string) — free-text operational rule or policy note attached to this unit CONTACT INFO (sub-object, default null) contactInfo.email (string, default null) — contact e-mail for this unit contactInfo.phone (string, default null) — contact phone number for this unit The entire contactInfo object defaults to null; set it as an object to activate contact fields. OPERATIONS timezone (string, default "Etc/GMT+3") — IANA/Etc timezone used to interpret schedules and reports workSchedule[] (Array, default []) — array of schedule objects describing shift windows; each element represents one shift period (e.g. day-of-week + start/end time). Shape is flexible — pass objects that match your system's scheduling conventions. dateOfActivation (Date, default null) — when this unit became (or will become) operational MEDIA avatar (string, default "") — URL of the unit's avatar / logo image AUDIT createdBy (ObjectId, ref Users) — user who created the record updatedBy (ObjectId, ref Users) — user who last updated the record deletedBy (ObjectId, ref Users) — user who soft-deleted the record LIFECYCLE status (boolean, default true) — soft-delete flag; list queries automatically filter to status:true via a pre-find hook, so deleted units are hidden by default createdAt (Date, default now), updatedAt (Date, auto-updated on any update operation) Every change is captured in the Audits collection via a change-stream watch. RELATIONSHIPS Account 1 ── N Unit — Unit.accountId links to the owning Account Group 1 ── N Unit — Unit.groupId links to the parent Group Users reference units via User.units[] (see smarttalks_users) — a user may belong to many units Tags reference units via Tag.units[] (see smarttalks_tags) — a tag can be restricted to specific units Queues may redirect to units on expiration via expiration.queue.unitId / expiration.queue.units[] (see smarttalks_queues) ACTIONS get { id } — fetch one unit by _id list { query, project?, options? } — Mongo filter. Common shapes: { accountId: "<id>" } — all units for an account { city: "São Paulo" } — units in a city { status: true } — active units only (default behaviour of the pre-find hook) { dateOfActivation: { $lte: new Date() } } — units that have already activated options supports { limit, skip } (default limit 10) create { payload } — required: label; groupId/accountId are server-injected from auth context optional: description, address, city, state, country, cnpj, link, ruleInfo, timezone, workSchedule, avatar, dateOfActivation, contactInfo update { id, payload } — mutate any mutable field; updatedBy is set server-side from auth context delete { id } — soft-delete: sets status:false and records deletedBy

smarttalks_user_activity

ChatGPT
User activity — APPEND-ONLY audit trail tracking operator-user lifecycle events (login/logout, availability transitions, queue assignments, socket connectivity, admin overrides). Each row is one event for one user, scoped to one Group + Account. Rows are NEVER updated or deleted by callers — this is an immutable audit log. IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected from JWT) — IMMUTABLE accountId (ObjectId, required, server-injected from JWT) — IMMUTABLE userId (ObjectId, required, server-injected from JWT auth._id) — the operator the event is about. NOTE: callers cannot record events on behalf of a different user; userId is always taken from the caller's own JWT on create. createdAt (Date, default Date.now) — when the event was recorded; the only timestamp on the row (no updatedAt because rows are immutable) EVENT event (string, required, ENUM) — one of: • "login" — user signed in • "logout" — user signed out (any reason) • "change_availability" — user manually changed availability (online/away/offline). payload carries { from, to }. • "auto_change_availability" — system auto-changed availability (e.g. idle timeout). payload carries { from, to }. • "admin_change_availability" — an admin forced a different availability on this user. payload carries { from, to } plus the actor. • "admin_force_logout" — an admin forcibly signed the user out • "interaction_distributed" — a Walktalk interaction was routed to this user (driven by queue distribution) • "get_next_on_queue" — user pulled the next interaction from a queue (open-distribution self-pick) • "socket_disconnected" — realtime socket dropped • "socket_reconnected" — realtime socket re-established • "disconnect_offline" — user marked offline due to prolonged disconnect payload (Object, free-form) — event-specific context. Common shapes: • availability events: { from: "online"|"away"|"offline", to: "..." } • interaction_distributed: { interactionId, queueId, channelId, ... } (varies by source) • admin_force_logout: { actorId } (the admin who triggered) The schema does not validate payload shape per event — store whatever the runtime emits. WRITE-SIDE GUARDS (enforced server-side; read-only failures from MCP perspective) • event is REQUIRED on create — server rejects with HTTP 400 "Missing payload or event" otherwise. • For change_availability and auto_change_availability, the server REJECTS rows where payload.from === payload.to (no-op transitions are considered invalid; HTTP 400 "Invalid payload"). • There is NO update or delete endpoint — this collection is append-only. READ-SIDE GUARDS (sanitisation by the upstream model) • query is restricted to a whitelist: only event, createdAt, userId are honoured. ANY OTHER FIELD IS SILENTLY DROPPED before the Mongo query runs. Use Mongo operators on those fields freely (e.g. { event: { $in: [...] } }, { createdAt: { $gte: "..." } }). • project (sent by MCP) is mapped onto the upstream's projection parameter, which is also whitelisted to: event, payload, createdAt, userId, groupId, accountId. Anything else is silently dropped. HOWEVER NOTE: this upstream reads req.body.projection while MCP sends req.body.project — projections passed via MCP project are CURRENTLY IGNORED by the upstream. List always returns all whitelisted fields. (Documented gotcha; not a blocker.) • options.limit is clamped to [1, 100] (default 10); options.skip defaults to 0. • Sort is fixed server-side to { createdAt: -1 } — newest first; you cannot change it via options.sort. • Tenant scope (groupId/accountId) is appended server-side from the JWT on every read. INDEXES Compound: { groupId: 1, accountId: 1, userId: 1, event: 1 } — per-user-per-event lookups Compound: { groupId: 1, accountId: 1, event: 1 } — tenant-wide event scans RELATIONSHIPS Group 1 ── N UserActivity.groupId (IMMUTABLE) Account 1 ── N UserActivity.accountId (IMMUTABLE) User (auth) 1 ── N UserActivity.userId — typically the operator the event describes (and, on create, ALWAYS the caller themselves) ACTIONS get { id } — fetch all activities for the user with id <id> (NOTE: the path /:userId filters by USER id, not by activity _id). Returns { count, items } of every activity for that user within the caller's tenant. There is no "fetch one activity row by its own _id" endpoint. list { query, project?, options? } — Mongo filter against the whitelist (event, createdAt, userId). Returns { count, items }. Common queries: { event: "login" } — all login events in tenant { event: { $in: ["login", "logout"] } } — session boundaries { userId: "<userObjectId>" } — same as get but via list semantics { createdAt: { $gte: "2026-04-01T00:00:00.000Z" } } — within a date range { event: "change_availability" } — all manual availability flips options supports { limit (1..100, default 10), skip (default 0) }. Sort is fixed to newest-first. create { payload } — append a new audit row for the CALLER. Required: payload.event (one of the enum values). Optional: payload.payload (event-specific context object). groupId/accountId/userId are ALL server-injected from the JWT and cannot be overridden — you cannot record events for another user via this endpoint. NOT EXPOSED This resource has NO update or delete action — the upstream API does not implement them. Audit rows are immutable. To "redact" or "correct" a row, write a compensating event via create.

smarttalks_user_activity_manage

ChatGPT
User activity — APPEND-ONLY audit trail tracking operator-user lifecycle events (login/logout, availability transitions, queue assignments, socket connectivity, admin overrides). Each row is one event for one user, scoped to one Group + Account. Rows are NEVER updated or deleted by callers — this is an immutable audit log. IDENTITY & TENANCY _id (ObjectId) groupId (ObjectId, required, server-injected from JWT) — IMMUTABLE accountId (ObjectId, required, server-injected from JWT) — IMMUTABLE userId (ObjectId, required, server-injected from JWT auth._id) — the operator the event is about. NOTE: callers cannot record events on behalf of a different user; userId is always taken from the caller's own JWT on create. createdAt (Date, default Date.now) — when the event was recorded; the only timestamp on the row (no updatedAt because rows are immutable) EVENT event (string, required, ENUM) — one of: • "login" — user signed in • "logout" — user signed out (any reason) • "change_availability" — user manually changed availability (online/away/offline). payload carries { from, to }. • "auto_change_availability" — system auto-changed availability (e.g. idle timeout). payload carries { from, to }. • "admin_change_availability" — an admin forced a different availability on this user. payload carries { from, to } plus the actor. • "admin_force_logout" — an admin forcibly signed the user out • "interaction_distributed" — a Walktalk interaction was routed to this user (driven by queue distribution) • "get_next_on_queue" — user pulled the next interaction from a queue (open-distribution self-pick) • "socket_disconnected" — realtime socket dropped • "socket_reconnected" — realtime socket re-established • "disconnect_offline" — user marked offline due to prolonged disconnect payload (Object, free-form) — event-specific context. Common shapes: • availability events: { from: "online"|"away"|"offline", to: "..." } • interaction_distributed: { interactionId, queueId, channelId, ... } (varies by source) • admin_force_logout: { actorId } (the admin who triggered) The schema does not validate payload shape per event — store whatever the runtime emits. WRITE-SIDE GUARDS (enforced server-side; read-only failures from MCP perspective) • event is REQUIRED on create — server rejects with HTTP 400 "Missing payload or event" otherwise. • For change_availability and auto_change_availability, the server REJECTS rows where payload.from === payload.to (no-op transitions are considered invalid; HTTP 400 "Invalid payload"). • There is NO update or delete endpoint — this collection is append-only. READ-SIDE GUARDS (sanitisation by the upstream model) • query is restricted to a whitelist: only event, createdAt, userId are honoured. ANY OTHER FIELD IS SILENTLY DROPPED before the Mongo query runs. Use Mongo operators on those fields freely (e.g. { event: { $in: [...] } }, { createdAt: { $gte: "..." } }). • project (sent by MCP) is mapped onto the upstream's projection parameter, which is also whitelisted to: event, payload, createdAt, userId, groupId, accountId. Anything else is silently dropped. HOWEVER NOTE: this upstream reads req.body.projection while MCP sends req.body.project — projections passed via MCP project are CURRENTLY IGNORED by the upstream. List always returns all whitelisted fields. (Documented gotcha; not a blocker.) • options.limit is clamped to [1, 100] (default 10); options.skip defaults to 0. • Sort is fixed server-side to { createdAt: -1 } — newest first; you cannot change it via options.sort. • Tenant scope (groupId/accountId) is appended server-side from the JWT on every read. INDEXES Compound: { groupId: 1, accountId: 1, userId: 1, event: 1 } — per-user-per-event lookups Compound: { groupId: 1, accountId: 1, event: 1 } — tenant-wide event scans RELATIONSHIPS Group 1 ── N UserActivity.groupId (IMMUTABLE) Account 1 ── N UserActivity.accountId (IMMUTABLE) User (auth) 1 ── N UserActivity.userId — typically the operator the event describes (and, on create, ALWAYS the caller themselves) ACTIONS get { id } — fetch all activities for the user with id <id> (NOTE: the path /:userId filters by USER id, not by activity _id). Returns { count, items } of every activity for that user within the caller's tenant. There is no "fetch one activity row by its own _id" endpoint. list { query, project?, options? } — Mongo filter against the whitelist (event, createdAt, userId). Returns { count, items }. Common queries: { event: "login" } — all login events in tenant { event: { $in: ["login", "logout"] } } — session boundaries { userId: "<userObjectId>" } — same as get but via list semantics { createdAt: { $gte: "2026-04-01T00:00:00.000Z" } } — within a date range { event: "change_availability" } — all manual availability flips options supports { limit (1..100, default 10), skip (default 0) }. Sort is fixed to newest-first. create { payload } — append a new audit row for the CALLER. Required: payload.event (one of the enum values). Optional: payload.payload (event-specific context object). groupId/accountId/userId are ALL server-injected from the JWT and cannot be overridden — you cannot record events for another user via this endpoint. NOT EXPOSED This resource has NO update or delete action — the upstream API does not implement them. Audit rows are immutable. To "redact" or "correct" a row, write a compensating event via create.

smarttalks_users

ChatGPT
Users, the HUMANS who log into the product. Every user is scoped to one Group (immutable) and one primary Account. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, IMMUTABLE after create) — parent tenant Group accountId (ObjectId) — primary Account accounts[] (ObjectId[]) — secondary Accounts the user can switch into units[] (ObjectId[]) — Unit ids the user belongs to (see smarttalks_units) department (ObjectId) — the Department this user belongs to; references smarttalks_departments PROFILE name, publicName — full name and short public-facing name email (string, required, validated format) — unique per group role (string) — free-text job title profile (enum: "admin" | "manager" | "supervisor" | "attendant", default "attendant") — capability tier phones[] (string[]) avatar (URL) emailSignature (string) AUTHENTICATION (treat as SECRETS — typically not returned on read) password (string, hashed with argon2) isPasswordHashed (boolean) — internal flag for migrations resetPasswordToken (string), resetPasswordExpires (Date) mfaEnabled (boolean, default false) mfaSecret (string, SECRET) — TOTP seed, only returned during MFA setup PERMISSIONS & CAPABILITIES permissionsGroups[] (ObjectId[]) — role bundles; see smarttalks_permissions_groups isGuider (boolean, default false) — internal operator flag receiveAutomatedAttendances (boolean, default false) — eligible for auto-routed interactions maxConcurrentCalls (number, default null) — per-user concurrent interaction cap (null = use queue default) unlimitedConcurrentCalls (boolean, default false) — overrides cap sttRedirect (boolean) — allow SmartTalks-redirect routing PRESENCE & ACTIVITY (written by the runtime, rarely in payloads) availability (enum: "online" | "away" | "offline", default "offline") onlineAt, awayAt, offlineAt (Date) — last transition to each state lastLogin, lastLogout (Date) lastIP, lastURL (string) SETTINGS (sub-object) — per-user UI & feature prefs settings.localization (string, default "en-US") — locale tag settings.timezone (string, default "Etc/GMT+3") settings.volume (number 0..1, default 0.5) — notification volume settings.orderBy (string, default "decreasingArrivalDate") — interaction list sort key settings.warningLimit (number, default 1) settings.isMobile (boolean) — client device hint settings.allInteractions (boolean, default true) — show all interactions view settings.myQueues (boolean, default true) — show "my queues" filter settings.groupInteractions (boolean, default true) — show group-scoped view settings.showNotification (boolean, default true) settings.showNotificationCount (boolean, default false) settings.showTimer (boolean, default false) settings.showTags (boolean, default false) settings.showChannel (boolean, default false) settings.showDivisionByQueues (boolean, default false) settings.showTotalInteractions (boolean, default false) settings.isBetaTester (boolean, default false) settings.hasFlowAccess (boolean, default false) — access to Flow editor settings.hasAgentAccess (boolean, default false) — access to Agent features settings.hasAgentBuilderAccess (boolean, default false) — can build agents settings.hasWorkflowAccess (boolean, default false) — access to Workflow editor LIFECYCLE status (boolean, default true) — soft-delete; list queries hide status:false by default createdAt, updatedAt, updatedBy (ObjectId) RELATIONSHIPS Group 1 ── N User.groupId (IMMUTABLE) Account 1 ── N User.accountId; may also reference secondary accounts via accounts[] User 1 ── N Tokens — list via smarttalks_tokens.list { query: { userId } } User N ── M PermissionsGroups via permissionsGroups[] (smarttalks_permissions_groups) User N ── M Units via units[] (smarttalks_units) User N ── 1 Department via department (smarttalks_departments); a department has many users in return ACTIONS get { id } — fetch one user list { query, project?, options? } — Mongo filter. Common: { email: "…" } — exact or regex { groupId, accountId } — typical tenant-scoping { status: true, profile: "…" } — by capability tier { availability: "online" } — currently connected { department: "<deptId>" } — users in a department options supports { limit, skip, sort } create { payload } — required: name, email, password, groupId, accountId optional: most other fields; omit mfaSecret/resetPasswordToken (server-managed) update { id, payload, project? } — any mutable field. groupId is IMMUTABLE — do NOT include. To rotate the password use payload.password (will be re-hashed). For partial settings updates, include only the subfields you want to change; merge semantics depend on the backend. delete { id } — soft-delete (sets status:false)

smarttalks_users_manage

ChatGPT
Users, the HUMANS who log into the product. Every user is scoped to one Group (immutable) and one primary Account. IDENTITY & TENANCY _id (ObjectId) id (number) — auto-incremented public identifier groupId (ObjectId, IMMUTABLE after create) — parent tenant Group accountId (ObjectId) — primary Account accounts[] (ObjectId[]) — secondary Accounts the user can switch into units[] (ObjectId[]) — Unit ids the user belongs to (see smarttalks_units) department (ObjectId) — the Department this user belongs to; references smarttalks_departments PROFILE name, publicName — full name and short public-facing name email (string, required, validated format) — unique per group role (string) — free-text job title profile (enum: "admin" | "manager" | "supervisor" | "attendant", default "attendant") — capability tier phones[] (string[]) avatar (URL) emailSignature (string) AUTHENTICATION (treat as SECRETS — typically not returned on read) password (string, hashed with argon2) isPasswordHashed (boolean) — internal flag for migrations resetPasswordToken (string), resetPasswordExpires (Date) mfaEnabled (boolean, default false) mfaSecret (string, SECRET) — TOTP seed, only returned during MFA setup PERMISSIONS & CAPABILITIES permissionsGroups[] (ObjectId[]) — role bundles; see smarttalks_permissions_groups isGuider (boolean, default false) — internal operator flag receiveAutomatedAttendances (boolean, default false) — eligible for auto-routed interactions maxConcurrentCalls (number, default null) — per-user concurrent interaction cap (null = use queue default) unlimitedConcurrentCalls (boolean, default false) — overrides cap sttRedirect (boolean) — allow SmartTalks-redirect routing PRESENCE & ACTIVITY (written by the runtime, rarely in payloads) availability (enum: "online" | "away" | "offline", default "offline") onlineAt, awayAt, offlineAt (Date) — last transition to each state lastLogin, lastLogout (Date) lastIP, lastURL (string) SETTINGS (sub-object) — per-user UI & feature prefs settings.localization (string, default "en-US") — locale tag settings.timezone (string, default "Etc/GMT+3") settings.volume (number 0..1, default 0.5) — notification volume settings.orderBy (string, default "decreasingArrivalDate") — interaction list sort key settings.warningLimit (number, default 1) settings.isMobile (boolean) — client device hint settings.allInteractions (boolean, default true) — show all interactions view settings.myQueues (boolean, default true) — show "my queues" filter settings.groupInteractions (boolean, default true) — show group-scoped view settings.showNotification (boolean, default true) settings.showNotificationCount (boolean, default false) settings.showTimer (boolean, default false) settings.showTags (boolean, default false) settings.showChannel (boolean, default false) settings.showDivisionByQueues (boolean, default false) settings.showTotalInteractions (boolean, default false) settings.isBetaTester (boolean, default false) settings.hasFlowAccess (boolean, default false) — access to Flow editor settings.hasAgentAccess (boolean, default false) — access to Agent features settings.hasAgentBuilderAccess (boolean, default false) — can build agents settings.hasWorkflowAccess (boolean, default false) — access to Workflow editor LIFECYCLE status (boolean, default true) — soft-delete; list queries hide status:false by default createdAt, updatedAt, updatedBy (ObjectId) RELATIONSHIPS Group 1 ── N User.groupId (IMMUTABLE) Account 1 ── N User.accountId; may also reference secondary accounts via accounts[] User 1 ── N Tokens — list via smarttalks_tokens.list { query: { userId } } User N ── M PermissionsGroups via permissionsGroups[] (smarttalks_permissions_groups) User N ── M Units via units[] (smarttalks_units) User N ── 1 Department via department (smarttalks_departments); a department has many users in return ACTIONS get { id } — fetch one user list { query, project?, options? } — Mongo filter. Common: { email: "…" } — exact or regex { groupId, accountId } — typical tenant-scoping { status: true, profile: "…" } — by capability tier { availability: "online" } — currently connected { department: "<deptId>" } — users in a department options supports { limit, skip, sort } create { payload } — required: name, email, password, groupId, accountId optional: most other fields; omit mfaSecret/resetPasswordToken (server-managed) update { id, payload, project? } — any mutable field. groupId is IMMUTABLE — do NOT include. To rotate the password use payload.password (will be re-hashed). For partial settings updates, include only the subfields you want to change; merge semantics depend on the backend. delete { id } — soft-delete (sets status:false)

smarttalks_views

ChatGPT
Views — VISUAL BOARD CONFIGURATIONS over CRM entity items. A view defines how a set of Items from one Entity is presented to the user. Currently only the "kanban" type is supported. A view is scoped to one Group + Account and always references an Entity and a field within that entity whose options become the kanban columns. INPUT SHAPE { resource, action, ...args } IDENTITY & TENANCY (applies to every view record) _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null, default null) — soft-delete; list queries filter deletedAt:null by default createdBy, updatedBy, deletedBy (ObjectId — User) CORE FIELDS title (string, required) — display name of the view description (string) — optional subtitle type (enum, default "kanban"): "kanban" — only kanban boards are currently supported entityId (ObjectId → Entity, required) — the entity whose Items this view renders fieldId (ObjectId → Entity.layout[].fields[]._id, required) — the select/status field whose options become the kanban columns totalityFieldId (ObjectId → Entity field, optional) — a currency/number field whose values are summed and shown as the column total dateFieldId (ObjectId → Entity field, default null) — the date field used for date-range filtering in the kanban KANBAN CARD STYLE (kanbanCardStyle[]) Defines where each field value appears on the kanban card: fieldId (string) — field _id, or the special strings "units", "createdAt", "clientId", "responsibleIds" position (enum): "top-left" | "top-right" | "top-center" | "bottom-left" | "bottom-right" | "bottom-center" | "center" | "center-left" | "center-right" createdAt (Date) — auto-set on add STAGE CONFIGURATION finalStages (string[]) — column option values that represent terminal/won states (items here are excluded from date-range defaults) exitStages (string[]) — column option values that represent exit/lost states exitReasons (object) — map of exitStage → string[] listing the reason choices when an item moves to that stage Example: { "Lost": ["Price", "Timing", "No budget"] } stageConfig (object[], update-only) — per-stage configuration. Each element: stageValue (string, required) — the column option value this config applies to linkedBlocks (string[], optional) — entity layout block ids surfaced when a card sits in this stage exitCriteria (object, optional) — gate for leaving this stage: requiredFields (string[]) — entity field ids that must be filled requiredBlocks (string[]) — entity layout block ids that must be complete requireMandatoryTasks (boolean, default true) — block exit while mandatory tasks are open taskTemplates (object[], optional) — tasks auto-created when a card ENTERS this stage: _id (string, optional) — supply to update an existing template; omit to create a new one. title (string, required), description (string, default ""), mandatory (boolean, default false), priority (enum: "low" | "normal" | "high" | "urgent", default "normal"), assigneeRule (enum: "item_owner" | "fixed" | "field", default "item_owner"), assigneeId (string | null) — used when assigneeRule = "fixed", assigneeFieldId (string | null) — used when assigneeRule = "field", dueDate (object, optional): kind (enum): "relative" — the only supported kind relative (object): value (number ≥ 1) unit (enum: "hours" | "days" | "business_days", default "days") anchor (enum: "entered_status" | "created_at" | "field", default "entered_status") anchorFieldId (string | null) — required when anchor = "field" KANBAN ACTIONS (kanbanActions[], update-only) Automation rules triggered by card events. Each rule: _id (string, required) — must be unique within the view (duplicates are rejected) enabled (boolean, required) trigger (enum): "days_in_status" | "card_age" | "due_date_approaching" | "due_date_overdue" | "no_responsible" | "field_empty" | "field_equals" DEPRECATED (accepted for backward compatibility, never fires): "on-enter" | "on-exit" REMOVED: "responsible_assigned" was dropped. Do not use it. condition (object, passthrough) — trigger-specific condition config actions[] (object[], 1–5 per rule, required) — automation actions to execute; each has a type plus type-specific fields. For type "set_kanban_stage": { targetViewId (ObjectId string, required), targetValue (string, required), delayMinutes? (int 0–10080), onlyIfEmpty? (boolean) } The target view must exist in the same account, be a DIFFERENT view, share the same entityId, and use a DIFFERENT fieldId. The server rejects violations. INDEXES (shape queries to hit these efficiently) (groupId, accountId) — always auto-scoped by server; no need to include in query entityId — filter by entity to find all views for a specific object type RELATIONSHIPS Group 1 ── N View.groupId Account 1 ── N View.accountId Entity 1 ── N View.entityId — the entity whose Items are rendered Entity field → View.fieldId — the kanban column-driver field Entity field → View.totalityFieldId — optional sum field Entity field → View.dateFieldId — optional date range field RESOURCES & ACTIONS view: list { skip?, limit?, sortBy?, sortOrder?, search?, entityId? } Paginated list of views for the caller's account. skip (number, default 0), limit (number, default 10) sortBy (string, default "updatedAt"), sortOrder ("asc" | "desc", default "desc") search (string) — case-insensitive regex against title and description entityId (ObjectId string) — filter to views for a specific entity Response: { items: View[], count: number } get { id } Fetch one view by its _id (full document including kanbanCardStyle, stages, actions). create { payload } Required: title, entityId, fieldId. Optional: description, type (default "kanban"), totalityFieldId, kanbanCardStyle[], finalStages[], exitStages[], exitReasons, dateFieldId. groupId/accountId are server-injected — do NOT include. update { id, payload } Replace mutable fields. Required in payload: entityId, fieldId (even if unchanged — server validates them). Optional: title, description, totalityFieldId, type, kanbanCardStyle[], finalStages[], exitStages[], exitReasons, dateFieldId, kanbanActions[]. groupId/accountId are IMMUTABLE — do NOT include. delete { id } Soft-delete: sets deletedAt = now, hidden from list. kanban { id, search?, searchFieldId?, dateFrom?, dateTo? } Fetch kanban board data (cards grouped by column) for a view. id (string, REQUIRED) — the view _id search (object or string) — advanced filter bag OR simple string search; the object form supports nested Mongo-style operators ($and, $or, _id, timeControl, etc.) searchFieldId (ObjectId string) — the entity field to match against when search is a plain string dateFrom, dateTo (string, "YYYY-MM-DD") — narrow the card date range. If neither is provided AND no advanced filter is active, defaults to the last 30 days. Response: cards grouped by column option value, with totality sums when totalityFieldId is set. dashboard { id, startDate?, endDate?, selectedEntity?, search?, searchFieldId? } Fetch aggregated dashboard metrics for a view. id (string, REQUIRED) — the view _id startDate, endDate (string) — ISO date strings to restrict the time window selectedEntity (string) — optional entity override for cross-entity dashboards search (string) — plain-text filter searchFieldId (ObjectId string) — the field to search against Response: aggregated counts/totals per column option. checkFieldUsage { fieldId } Check whether a specific entity field (by _id) is referenced in any active view. Useful before deleting a field to detect breaking changes. fieldId (string, REQUIRED) — the entity field _id to inspect Response: { result: boolean | object } — truthy if the field is in use EXAMPLES view.list { limit: 20, entityId: "<entityId>" } view.get { id: "<viewId>" } view.create { payload: { title: "Sales Pipeline", entityId: "<entityId>", fieldId: "<stageFieldId>", type: "kanban", finalStages: ["Won"], exitStages: ["Lost"] } } view.update { id: "<viewId>", payload: { title: "Updated Pipeline", entityId: "<entityId>", fieldId: "<stageFieldId>", exitReasons: { "Lost": ["Price", "Timing"] } } } view.delete { id: "<viewId>" } view.kanban { id: "<viewId>", dateFrom: "2026-01-01", dateTo: "2026-01-31" } view.kanban { id: "<viewId>", search: { "fields": { "$elemMatch": { "fieldId": "<fieldId>", "value": "High" } } } } view.dashboard { id: "<viewId>", startDate: "2026-01-01", endDate: "2026-01-31" } view.checkFieldUsage { fieldId: "<fieldId>" }

smarttalks_views_manage

ChatGPT
Views — VISUAL BOARD CONFIGURATIONS over CRM entity items. A view defines how a set of Items from one Entity is presented to the user. Currently only the "kanban" type is supported. A view is scoped to one Group + Account and always references an Entity and a field within that entity whose options become the kanban columns. INPUT SHAPE { resource, action, ...args } IDENTITY & TENANCY (applies to every view record) _id (ObjectId) groupId (ObjectId, required, server-injected) — parent tenant Group accountId (ObjectId, required, server-injected) — parent Account createdAt, updatedAt (Date) — managed automatically deletedAt (Date | null, default null) — soft-delete; list queries filter deletedAt:null by default createdBy, updatedBy, deletedBy (ObjectId — User) CORE FIELDS title (string, required) — display name of the view description (string) — optional subtitle type (enum, default "kanban"): "kanban" — only kanban boards are currently supported entityId (ObjectId → Entity, required) — the entity whose Items this view renders fieldId (ObjectId → Entity.layout[].fields[]._id, required) — the select/status field whose options become the kanban columns totalityFieldId (ObjectId → Entity field, optional) — a currency/number field whose values are summed and shown as the column total dateFieldId (ObjectId → Entity field, default null) — the date field used for date-range filtering in the kanban KANBAN CARD STYLE (kanbanCardStyle[]) Defines where each field value appears on the kanban card: fieldId (string) — field _id, or the special strings "units", "createdAt", "clientId", "responsibleIds" position (enum): "top-left" | "top-right" | "top-center" | "bottom-left" | "bottom-right" | "bottom-center" | "center" | "center-left" | "center-right" createdAt (Date) — auto-set on add STAGE CONFIGURATION finalStages (string[]) — column option values that represent terminal/won states (items here are excluded from date-range defaults) exitStages (string[]) — column option values that represent exit/lost states exitReasons (object) — map of exitStage → string[] listing the reason choices when an item moves to that stage Example: { "Lost": ["Price", "Timing", "No budget"] } stageConfig (object[], update-only) — per-stage configuration. Each element: stageValue (string, required) — the column option value this config applies to linkedBlocks (string[], optional) — entity layout block ids surfaced when a card sits in this stage exitCriteria (object, optional) — gate for leaving this stage: requiredFields (string[]) — entity field ids that must be filled requiredBlocks (string[]) — entity layout block ids that must be complete requireMandatoryTasks (boolean, default true) — block exit while mandatory tasks are open taskTemplates (object[], optional) — tasks auto-created when a card ENTERS this stage: _id (string, optional) — supply to update an existing template; omit to create a new one. title (string, required), description (string, default ""), mandatory (boolean, default false), priority (enum: "low" | "normal" | "high" | "urgent", default "normal"), assigneeRule (enum: "item_owner" | "fixed" | "field", default "item_owner"), assigneeId (string | null) — used when assigneeRule = "fixed", assigneeFieldId (string | null) — used when assigneeRule = "field", dueDate (object, optional): kind (enum): "relative" — the only supported kind relative (object): value (number ≥ 1) unit (enum: "hours" | "days" | "business_days", default "days") anchor (enum: "entered_status" | "created_at" | "field", default "entered_status") anchorFieldId (string | null) — required when anchor = "field" KANBAN ACTIONS (kanbanActions[], update-only) Automation rules triggered by card events. Each rule: _id (string, required) — must be unique within the view (duplicates are rejected) enabled (boolean, required) trigger (enum): "days_in_status" | "card_age" | "due_date_approaching" | "due_date_overdue" | "no_responsible" | "field_empty" | "field_equals" DEPRECATED (accepted for backward compatibility, never fires): "on-enter" | "on-exit" REMOVED: "responsible_assigned" was dropped. Do not use it. condition (object, passthrough) — trigger-specific condition config actions[] (object[], 1–5 per rule, required) — automation actions to execute; each has a type plus type-specific fields. For type "set_kanban_stage": { targetViewId (ObjectId string, required), targetValue (string, required), delayMinutes? (int 0–10080), onlyIfEmpty? (boolean) } The target view must exist in the same account, be a DIFFERENT view, share the same entityId, and use a DIFFERENT fieldId. The server rejects violations. INDEXES (shape queries to hit these efficiently) (groupId, accountId) — always auto-scoped by server; no need to include in query entityId — filter by entity to find all views for a specific object type RELATIONSHIPS Group 1 ── N View.groupId Account 1 ── N View.accountId Entity 1 ── N View.entityId — the entity whose Items are rendered Entity field → View.fieldId — the kanban column-driver field Entity field → View.totalityFieldId — optional sum field Entity field → View.dateFieldId — optional date range field RESOURCES & ACTIONS view: list { skip?, limit?, sortBy?, sortOrder?, search?, entityId? } Paginated list of views for the caller's account. skip (number, default 0), limit (number, default 10) sortBy (string, default "updatedAt"), sortOrder ("asc" | "desc", default "desc") search (string) — case-insensitive regex against title and description entityId (ObjectId string) — filter to views for a specific entity Response: { items: View[], count: number } get { id } Fetch one view by its _id (full document including kanbanCardStyle, stages, actions). create { payload } Required: title, entityId, fieldId. Optional: description, type (default "kanban"), totalityFieldId, kanbanCardStyle[], finalStages[], exitStages[], exitReasons, dateFieldId. groupId/accountId are server-injected — do NOT include. update { id, payload } Replace mutable fields. Required in payload: entityId, fieldId (even if unchanged — server validates them). Optional: title, description, totalityFieldId, type, kanbanCardStyle[], finalStages[], exitStages[], exitReasons, dateFieldId, kanbanActions[]. groupId/accountId are IMMUTABLE — do NOT include. delete { id } Soft-delete: sets deletedAt = now, hidden from list. kanban { id, search?, searchFieldId?, dateFrom?, dateTo? } Fetch kanban board data (cards grouped by column) for a view. id (string, REQUIRED) — the view _id search (object or string) — advanced filter bag OR simple string search; the object form supports nested Mongo-style operators ($and, $or, _id, timeControl, etc.) searchFieldId (ObjectId string) — the entity field to match against when search is a plain string dateFrom, dateTo (string, "YYYY-MM-DD") — narrow the card date range. If neither is provided AND no advanced filter is active, defaults to the last 30 days. Response: cards grouped by column option value, with totality sums when totalityFieldId is set. dashboard { id, startDate?, endDate?, selectedEntity?, search?, searchFieldId? } Fetch aggregated dashboard metrics for a view. id (string, REQUIRED) — the view _id startDate, endDate (string) — ISO date strings to restrict the time window selectedEntity (string) — optional entity override for cross-entity dashboards search (string) — plain-text filter searchFieldId (ObjectId string) — the field to search against Response: aggregated counts/totals per column option. checkFieldUsage { fieldId } Check whether a specific entity field (by _id) is referenced in any active view. Useful before deleting a field to detect breaking changes. fieldId (string, REQUIRED) — the entity field _id to inspect Response: { result: boolean | object } — truthy if the field is in use EXAMPLES view.list { limit: 20, entityId: "<entityId>" } view.get { id: "<viewId>" } view.create { payload: { title: "Sales Pipeline", entityId: "<entityId>", fieldId: "<stageFieldId>", type: "kanban", finalStages: ["Won"], exitStages: ["Lost"] } } view.update { id: "<viewId>", payload: { title: "Updated Pipeline", entityId: "<entityId>", fieldId: "<stageFieldId>", exitReasons: { "Lost": ["Price", "Timing"] } } } view.delete { id: "<viewId>" } view.kanban { id: "<viewId>", dateFrom: "2026-01-01", dateTo: "2026-01-31" } view.kanban { id: "<viewId>", search: { "fields": { "$elemMatch": { "fieldId": "<fieldId>", "value": "High" } } } } view.dashboard { id: "<viewId>", startDate: "2026-01-01", endDate: "2026-01-31" } view.checkFieldUsage { fieldId: "<fieldId>" }

smarttalks_workflows

ChatGPT
Workflows façade. Addresses workflows, draft versions, executions, and the node/tool catalog. INPUT SHAPE { resource, action, ...args } RESOURCES & ACTIONS workflow: list | get | delete | duplicate | export | import version: list | get | create | update | delete | publish execution: list | get | pause | resume | cancel node: list | get catalog — full JSON Schema per node type tool: list agent-callable subset DATA MODEL — IMPORTANT There are TWO collections for workflow data: - workflows (what workflow.list hits): ONLY published workflows. A workflow only appears here once one of its versions has been published. - workflow_versions (what version.list hits): both drafts and published history. All unpublished/draft work lives here. To see ALL workflows (incl. drafts) use version.list, e.g.: version.list { unique: true } latest version per workflowId (drafts + published) version.list { unique: true, published: false } drafts only version.list { workflowId, unique: false } full history of one workflow workflow.list is the right choice only when you specifically want live/published workflows. KEY PATTERNS - version.get { versionId, select: "summary" | "node" | "full", nodeId? } "summary" returns label/description + counts + a minimal node list + all edges. IMPORTANT: nodes[] is insertion order, NOT execution order. To reason about flow, follow edges[] (source → target, plus sourceHandle for branches). Start from trigger nodes (nodeType ending in "_trigger") and walk edges forward. "node" returns a single node by nodeId (requires nodeId). "full" returns the raw snapshot (big — prefer summary first). - version.update { versionId, patch: { op, ... }, expectedUpdatedAt?, validate_only?, commitMessage? } patch ops: upsert_node | remove_node | update_node_data | move_node | upsert_edge | remove_edge | connect_branch | disconnect | set_metadata | set_commit_message expectedUpdatedAt guards against lost updates: if the draft was modified since, returns { error: "conflict" }. validate_only runs apply+validation but skips the PATCH. - version.update { versionId, snapshot }: full-replace escape hatch. - workflow.trigger / version.test are side-effecting and gated by env flags.

smarttalks_workflows_manage

ChatGPT
Workflows façade. Addresses workflows, draft versions, executions, and the node/tool catalog. INPUT SHAPE { resource, action, ...args } RESOURCES & ACTIONS workflow: list | get | delete | duplicate | export | import version: list | get | create | update | delete | publish execution: list | get | pause | resume | cancel node: list | get catalog — full JSON Schema per node type tool: list agent-callable subset DATA MODEL — IMPORTANT There are TWO collections for workflow data: - workflows (what workflow.list hits): ONLY published workflows. A workflow only appears here once one of its versions has been published. - workflow_versions (what version.list hits): both drafts and published history. All unpublished/draft work lives here. To see ALL workflows (incl. drafts) use version.list, e.g.: version.list { unique: true } latest version per workflowId (drafts + published) version.list { unique: true, published: false } drafts only version.list { workflowId, unique: false } full history of one workflow workflow.list is the right choice only when you specifically want live/published workflows. KEY PATTERNS - version.get { versionId, select: "summary" | "node" | "full", nodeId? } "summary" returns label/description + counts + a minimal node list + all edges. IMPORTANT: nodes[] is insertion order, NOT execution order. To reason about flow, follow edges[] (source → target, plus sourceHandle for branches). Start from trigger nodes (nodeType ending in "_trigger") and walk edges forward. "node" returns a single node by nodeId (requires nodeId). "full" returns the raw snapshot (big — prefer summary first). - version.update { versionId, patch: { op, ... }, expectedUpdatedAt?, validate_only?, commitMessage? } patch ops: upsert_node | remove_node | update_node_data | move_node | upsert_edge | remove_edge | connect_branch | disconnect | set_metadata | set_commit_message expectedUpdatedAt guards against lost updates: if the draft was modified since, returns { error: "conflict" }. validate_only runs apply+validation but skips the PATCH. - version.update { versionId, snapshot }: full-replace escape hatch. - workflow.trigger / version.test are side-effecting and gated by env flags.

Capabilities

Writes

App Stats

73

Tools

ChatGPT

Platforms

Works with

ChatGPT

Data refreshed daily