SmartTalks.ai
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.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- AI Chat & Messaging Agents
- Secondary Subcategories
- None listed
- Brand
- SmartTalks.ai
- Access
- Account required
- First tracked
- 2026-05-28
- Tool count
- 73
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is visible.
ChatGPT Plugin Discoverability Score
ChatGPT organic discovery is not live yet
SmartTalks.ai is tracked in the ChatGPT Plugin registry. Public organic-discovery measurement is not live for ChatGPT yet, so there is no score to publish today.
Get notified when your score goes live
Enter your work email and we’ll notify you when ChatGPT Plugin organic discovery scoring launches.
No spam. Unsubscribe any time.
Competing in ChatGPT AI Chat & Messaging Agents
View CategoryHow the Discoverability Score works
Organic discovery scoring for SmartTalks.ai on ChatGPT is not live yet. The score will use measured agent conversations when it launches.
Organic discovery scoring is pending. Your Plugin score will appear on this scale when measurement goes live.
FoundDiagnostic
Whether Claude found your Plugin in connector search. It must be Found before it can reach the picker, but the score counts picker appearances—not search results.
PickedMain score
How often your Plugin appeared in the picker, or Claude invoked it directly, across contested conversations. This percentage is the Discoverability Score; the headline number is rounded.
PositionedDiagnostic
What position your Plugin appeared in when it was shown in the picker. This shows prominence, but it does not affect the score.
73 tools agents can invoke
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
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
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
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
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
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
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
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
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
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
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_channels
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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_interactions
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
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
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
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
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 can*Scope 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
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 can*Scope 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
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
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
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
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
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
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
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
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
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
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
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are SmartTalks.ai alternatives on ChatGPT?
As of 2026-08-14, SmartTalks.ai competes with Alhena AI, BubblaV AI Chatbot, iZap, LetsBot, Nexvio AI, Quickchat AI, ScalperIntel AI, STORM Brains4Ai, Ventor in ChatGPT AI Chat & Messaging Agents, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.