Connection check
verified live · 20h ago
flowcastle
Build, edit, and deploy Telegram bots on FlowCastle's hosted visual flow platform.
Tools
36
GitHub stars
2
Installs / wk
—
Licence
MIT
Transport
streamable-http
Last checked
20h ago
Tools & capabilities
36 toolsRead from the running server on 20h ago.
apply_actions
can modify data
flowIdactions*applicationIdconversationId
Validate and apply a batch of flow-builder actions — the single write path for editing flows, blocks, variables, broadcasts, sequences, and folders. Call this directly; a separate… Validate and apply a batch of flow-builder actions — the single write path for editing flows, blocks, variables, broadcasts, sequences, and folders. Call this directly; a separate validate_actions call beforehand is unnecessary. Broadcasts have no dedicated tool and are managed here: create_broadcast makes a DRAFT (it owns its flow via data.flowId — add the message blocks in the same batch, no separate create_flow), optionally with create_recurrence_schedule + attach_recurrence_to_broadcast for recurring; a later update_broadcast with status SCHEDULED (and scheduledAt for one-shots) is what actually schedules/sends it. The full recipe is in get_action_schema under `broadcasts`. DESTRUCTIVE: the batch may include delete_block, delete_link, delete_flow, delete_variable, delete_operation, and delete_broadcast. Confirm with the user before applying deletions. delete_operation also removes the operation's hidden graph flow and run history; delete_broadcast also removes the broadcast's delivery history and its content flow, and neither can be undone. IRREVERSIBLE SIDE EFFECTS: run_operation starts a real operation run, which may send broadcasts to real contacts and write application variables. It cannot be undone or recalled, is not idempotent, and is available only through this tool — confirm with the user before applying a batch containing one, and never blindly retry a timed-out call that did. Validation always runs first and an invalid batch applies nothing. Execution is NOT atomic, however: if an action fails mid-batch, the actions before it stay applied and execution stops — re-read state with get_flow_context before retrying rather than blindly resending the batch. Not idempotent — resending a batch of create_* actions creates duplicates. Read get_action_schema for the action contract and get_design_guidelines before any structural edit. Returns { success, changes, errors, warnings, actionId } plus an idRemap mapping placeholder ids to the real ids that were created. Applying does NOT publish. Edits land on the draft graph and connected bots keep serving the previously published version until deploy_application runs — finish a round of edits, then deploy.
create_application
nameskipDefaultFlowspreferredLanguage
Create a new application (workspace) owned by the caller. Requires a personal API key (usr_...) — application-scoped keys cannot create applications. Seeds default flows unless ski… Create a new application (workspace) owned by the caller. Requires a personal API key (usr_...) — application-scoped keys cannot create applications. Seeds default flows unless skipDefaultFlows is true. Creates persistent state and is NOT idempotent: calling it twice creates two applications. Returns the new application id, which you then pass as applicationId to the other tools.
create_contact
botIdemailphonestatuslastNameusername
+4
Create a contact manually — for imports or externally-sourced audiences; contacts who message a bot are created automatically. Requires the manage_broadcasts permission. platformId… Create a contact manually — for imports or externally-sourced audiences; contacts who message a bot are created automatically. Requires the manage_broadcasts permission. platformId must be unique within the bot (duplicate fails with 409); botId may be omitted only when the application has exactly one bot. The variables map takes variable NAMES (or full folder paths when a name is ambiguous) — not ids — and unknown names fail with 422. NOT idempotent: retrying a success creates nothing new only because the duplicate platformId is rejected.
deploy_application
botIdsapplicationId
Publish the workspace to its bots — the API equivalent of the dashboard's Deploy button. This is the step that makes edits live. apply_actions writes to the DRAFT graph. Until this… Publish the workspace to its bots — the API equivalent of the dashboard's Deploy button. This is the step that makes edits live. apply_actions writes to the DRAFT graph. Until this runs the connected bots keep serving the previously published version, so a change that looks applied has no effect for real users. Deploy after a batch of edits (and after run_flow_autotest passes), not after every single action. Publishes the ACTIVE version to every active bot of the application; pass botIds to publish to a subset. Rolling back to an older version is a dashboard action and is deliberately not available here. Delivery is asynchronous: a bot listed as "queued" was handed to the deploy queue, not confirmed restarted. Returns { deployed, versionId, bots[], queuedCount, failedCount, error } — check `error` and each bot's `status`, because a version can be marked published while no runtime received it. Safe to repeat: deploying twice republishes the same version rather than duplicating anything. It does change what real users see, so confirm with the user before publishing edits they have not reviewed. Requires the manage_automation permission.
get_action_schema
read-only
topicsactionsblockTypesactionKinds
Return the action-authoring contract that apply_actions batches are validated against. Read-only, needs no API key. Called with NO arguments it returns a compact INDEX: every creat… Return the action-authoring contract that apply_actions batches are validated against. Read-only, needs no API key. Called with NO arguments it returns a compact INDEX: every creatable block type, action and topic with one line saying when you need it. Call it a second time naming only what the bot you are building actually uses — { blockTypes: ["AI_TOOL_ROUTER"], topics: ["knowledgeBases"] } — and you get those contracts in full, plus the batch contract, placeholder rules and the invariants that apply to every batch. The whole document is far too large to read at once; the index exists so you never have to.
get_application_context
read-only
applicationId
Return the full application-level automation context in one read-only call: every flow (with folders), connected bots, variables, sequences, and operations. This is the broad orien… Return the full application-level automation context in one read-only call: every flow (with folders), connected bots, variables, sequences, and operations. This is the broad orientation call — prefer get_workspace_summary when you only need names and counts, since this response grows with workspace size. Operation graphs are hidden flows and appear only in the operations list, never in flows.
get_block_details
read-only
flowId*blockId*applicationId
Return the complete contents of one block: block data, action configs, HTTP request bodies, custom-code files, triggers, menu payloads, and media paths. Read-only. This is the heav… Return the complete contents of one block: block data, action configs, HTTP request bodies, custom-code files, triggers, menu payloads, and media paths. Read-only. This is the heaviest read in the API — call get_flow_context first to find the block you need rather than walking a flow block by block. Always read a block before updating it, since update_block replaces the fields you send.
get_broadcast_analytics
read-only
endDatestartDatebroadcastId*applicationId
Return engagement analytics for a broadcast: delivery breakdown by status plus per-message-block sent and clicked counts for its flow, over an optional date window. Read-only. Sent… Return engagement analytics for a broadcast: delivery breakdown by status plus per-message-block sent and clicked counts for its flow, over an optional date window. Read-only. Sent counts reflect messages attempted, not confirmed deliveries.
get_broadcast_details
read-only
broadcastId*applicationId
Return full details for a single broadcast: status, schedule, recurrence rule, linked flow, and delivery breakdown by status. Read-only. Call list_broadcasts first to find the broa… Return full details for a single broadcast: status, schedule, recurrence rule, linked flow, and delivery breakdown by status. Read-only. Call list_broadcasts first to find the broadcastId. For per-message-block engagement stats use get_broadcast_analytics instead.
get_contact
read-only
contactId*applicationId
Return one contact's full profile plus every contact-variable value stored for them. Read-only. Values may hold personal data; variables of type SECRET are always redacted. Call li… Return one contact's full profile plus every contact-variable value stored for them. Read-only. Values may hold personal data; variables of type SECRET are always redacted. Call list_contacts first to find the contactId.
get_contact_activity
read-only
limitendDatecontactId*startDateapplicationIdincludeFlowRuns
Return one contact's engagement history: goals they achieved and buttons they clicked, newest first, plus all-time goal totals. Read-only. This is the per-contact companion to get_… Return one contact's engagement history: goals they achieved and buttons they clicked, newest first, plus all-time goal totals. Read-only. This is the per-contact companion to get_broadcast_analytics (which is aggregate). Clicks are inline/menu button presses inside the bot — typed replies, commands and website visits never appear. Goals and clicks are capped separately by `limit`; goalsTruncated/clicksTruncated say when older events exist. Call list_contacts first to find the contactId.
get_design_guidelines
read-only
Return the flow-design rules that validation does NOT enforce: when to split a branch into its own flow, how navigation and menus must be wired, and worked examples. Read-only, tak… Return the flow-design rules that validation does NOT enforce: when to split a branch into its own flow, how navigation and menus must be wired, and worked examples. Read-only, takes no arguments, and needs no API key. Read this before any structural edit (new blocks, new branches, new flows) — a batch can pass validate_actions and still be badly structured, and these rules are what catch that.
get_flow_context
read-only
flowId*applicationId
Return one flow's graph topology: its blocks, how they link, and a short summary per block. Read-only. Deliberately omits block data and action configs to stay cheap — once you kno… Return one flow's graph topology: its blocks, how they link, and a short summary per block. Read-only. Deliberately omits block data and action configs to stay cheap — once you know which block matters, call get_block_details for its full contents. This is the normal first step before editing an existing flow.
get_flow_example
read-only
id*includeSchemaExample
Return one reusable flow example by id, optionally with a complete action batch you can adapt and pass to apply_actions. Read-only, needs no API key. Call search_flow_examples firs… Return one reusable flow example by id, optionally with a complete action batch you can adapt and pass to apply_actions. Read-only, needs no API key. Call search_flow_examples first to find the id.
get_funnel_analytics
read-only
applicationId
Return the measured conversion funnel of the workspace's busiest entry flow over the last 30 days: how many new contacts entered, which blocks they reached, where most of them stop… Return the measured conversion funnel of the workspace's busiest entry flow over the last 30 days: how many new contacts entered, which blocks they reached, where most of them stop, which buttons lead nowhere (pressed then silence), and how many recorded a goal. Read-only, computed on demand from the execution log — the same numbers Pulse's recommendations are grounded in. The result carries the follow-up sequence playbook; read both before proposing any marketing/nurture sequence, and propose nothing when the funnel is healthy or too thin.
get_module_catalog
read-only
applicationId
Return a compact index of both installed and available marketplace modules, with each module's key, versions, description, actions, and triggers. Read-only. Start here when you nee… Return a compact index of both installed and available marketplace modules, with each module's key, versions, description, actions, and triggers. Read-only. Start here when you need a capability the core action kinds do not cover; then call get_module_details for the exact input fields of one module, and install_module to add it. Returns a summary only — action input fields and setup requirements come from get_module_details.
get_module_details
read-only
moduleKey*applicationIdmoduleVersion
Return everything needed to use one module: action input fields and their types, trigger configuration, manual setup fields (credentials an operator must fill in the dashboard), an… Return everything needed to use one module: action input fields and their types, trigger configuration, manual setup fields (credentials an operator must fill in the dashboard), and references to already-installed actions. Read-only. Call get_module_catalog first to obtain moduleKey, and call this again after install_module to read the installed action references you need when drafting actions. An unknown moduleKey does not raise — the response carries an `error` string plus `availableModules` listing valid keys and versions.
get_variable_context
read-only
limitqueryscopeapplicationIdincludeValues
Search variable definitions by scope and keyword. Read-only. Returns { variables, total, returned, truncated } — compare returned against total to detect a cut-off result set and r… Search variable definitions by scope and keyword. Read-only. Returns { variables, total, returned, truncated } — compare returned against total to detect a cut-off result set and re-call with a higher limit. Values are withheld unless includeValues is true; variables marked secret stay redacted either way. Use the returned ids in `{{var|<id>}}` references.
get_workspace_summary
read-only
flowIdapplicationId
Return a compact application, flow, sequence, operation, and bot summary — the cheapest way to orient in a workspace. Read-only, no side effects. Deliberately omits variables and f… Return a compact application, flow, sequence, operation, and bot summary — the cheapest way to orient in a workspace. Read-only, no side effects. Deliberately omits variables and full flow graphs: use get_variable_context for variables, get_flow_context for a flow's topology, and get_application_context when you need flows, bots, and variables together.
install_module
moduleKey*applicationIdmoduleVersion*
Install an exact marketplace module version into an application and create any missing installed-template actions. Requires the manage_automation permission. Call get_module_catalo… Install an exact marketplace module version into an application and create any missing installed-template actions. Requires the manage_automation permission. Call get_module_catalog first to select the module and version, then get_module_details after installation to inspect setup requirements and installed action references. Safe to re-run: installing a version that is already installed only fills in missing template actions rather than duplicating them. Modules with manual setup fields still need an operator to enter credentials in the dashboard before their actions will run.
list_applications
read-only
List the applications this API key can access, with the caller role and the permissions it grants. Start here when using a personal API key (usr_...): every other tool needs an exp… List the applications this API key can access, with the caller role and the permissions it grants. Start here when using a personal API key (usr_...): every other tool needs an explicit applicationId, which this tool supplies. Read-only, takes no arguments. Returns an array of { id, name, role, permissions }; an empty array means the key is valid but belongs to no application yet.
list_broadcasts
read-only
pagebotIdlimitstatusisRecurringapplicationId
List broadcasts in the application with status, schedule, and delivery counts. Read-only. Filters combine as AND. Note that delivery counts report messages attempted, not confirmed… List broadcasts in the application with status, schedule, and delivery counts. Read-only. Filters combine as AND. Note that delivery counts report messages attempted, not confirmed deliveries. Use get_broadcast_details for one broadcast's full breakdown. To CREATE or SEND a broadcast use apply_actions: create_broadcast makes a draft, update_broadcast (status SCHEDULED) schedules/sends it — see get_action_schema under `broadcasts`.
list_contacts
read-only
pagebotIdlimitsearchstatusisActive
+1
List and search contacts in the application, paginated, newest first. Read-only. Filters combine as AND; search matches name, username, email, phone, and platformId. Returns compac… List and search contacts in the application, paginated, newest first. Read-only. Filters combine as AND; search matches name, username, email, phone, and platformId. Returns compact contact summaries without variable values — use get_contact for one contact's variables. Remember platformId is unique only per bot, so the same person talking to two bots appears as two contacts.
list_event_contacts
read-only
kind*pagebotIdlimitsearchblockId
+7
The reverse lookup: which contacts triggered one analytics event — achieved a goal (kind GOAL + goalKey), clicked a button (BUTTON_CLICK + blockId, optionally buttonId/buttonIndex)… The reverse lookup: which contacts triggered one analytics event — achieved a goal (kind GOAL + goalKey), clicked a button (BUTTON_CLICK + blockId, optionally buttonId/buttonIndex), were sent a block (BLOCK_SENT + blockId), or received a broadcast (BROADCAST_DELIVERED + broadcastId). Read-only, paginated, ordered by each contact's most recent matching event. Runs as SQL over the event tables, so it is safe on large workspaces — prefer it over paging list_contacts and checking each one. Omitting goalKey for kind GOAL fails with the list of known goal keys, which is the cheapest way to discover them.
list_watched_groups
read-only
botIdapplicationId
List the group/channel chats a telegram_mtproto userbot monitors. Read-only. The watched list is the single source of truth for which chats the userbot processes: messages from unl… List the group/channel chats a telegram_mtproto userbot monitors. Read-only. The watched list is the single source of truth for which chats the userbot processes: messages from unlisted group/channel chats are dropped (fail closed) and their contacts never materialize; DMs always pass. botId may be omitted when the application has exactly one userbot.
read_messages
read-only
botIdlimitcursorendDatecontactIddirection
+2
Read the message transcript: what users sent the bot and what the bot sent back, newest first. Source is the runtime's own message ledger, written by the bot as it handled each tur… Read the message transcript: what users sent the bot and what the bot sent back, newest first. Source is the runtime's own message ledger, written by the bot as it handled each turn — inbound messages are recorded before any routing decision, so messages that matched no trigger are here too. Filter by contactId for one conversation, botId for one channel, direction for one side, and startDate/endDate for a window. Page further into the past by passing the returned nextCursor back as `cursor`. Text only. A photo or document contributes its caption; the file is not stored. Button taps are NOT messages and never appear here — use get_contact_activity for those. Message wording is redacted after the content retention window (the response says how long), leaving text null on old rows. Read-only. Requires the view_logs permission: this is raw personal message content of your end users.
run_flow_autotest
read-only
flowIds*scenariosapplicationId
Runs deterministic behavioural tests against flows that are ALREADY applied (compiles them to an AST and simulates a user). Call after apply_actions to verify a build; read `summar… Runs deterministic behavioural tests against flows that are ALREADY applied (compiles them to an AST and simulates a user). Call after apply_actions to verify a build; read `summary` and the failed checks, patch with apply_actions, re-run. Mutates nothing. The smoke layer runs on its own with no input: it walks every entry, taps every button, answers every input step, and reports crashes, dead buttons, unresolved placeholders, and values the bot failed to store. Pass `scenarios` to also replay specific user journeys (at most 6) — that is the only way to assert exact texts or exact stored values. Returns { passed, smoke, scenarios, summary }. `passed` is false when any check or scenario failed; a `summary` saying coverage is "none" means nothing was testable, so a green verdict there proves nothing. Nothing is sent to real users and no state is written.
search_flow_examples
read-only
tagslimitquery
Search the library of reusable flow examples covering common business cases (lead capture, onboarding, payments, reminders). Read-only, needs no API key. Returns compact matches —… Search the library of reusable flow examples covering common business cases (lead capture, onboarding, payments, reminders). Read-only, needs no API key. Returns compact matches — id, title, summary, tags — with no flow body; pass an id to get_flow_example for the full example. Calling it with no arguments returns the top examples, and a query matching nothing returns an empty list rather than an error.
search_flows
read-only
limitquery*applicationId
Find every flow and block whose contents contain a keyword. Read-only. Searches message text and its translations, button labels and URLs, action names and configs, action input/ou… Find every flow and block whose contents contain a keyword. Read-only. Searches message text and its translations, button labels and URLs, action names and configs, action input/output field paths and values, condition operands, trigger commands and payloads, custom-code files, and flow names and descriptions. A keyword matching a VARIABLE NAME also returns the blocks that reference that variable, which plain text search cannot do because blocks store variable ids, not names. Use this instead of walking flows with get_flow_context when you know what the content says but not where it lives. Broadcast-backed flows and operation graphs are excluded — use list_broadcasts and the operations tools for those.
send_flow_to_contacts
flowId*contactIds*applicationId
Run an EXISTING interactive flow for each listed contact right now, outside any trigger — as if each of them had just triggered it. Use it when the WHOLE message is the flow — its… Run an EXISTING interactive flow for each listed contact right now, outside any trigger — as if each of them had just triggered it. Use it when the WHOLE message is the flow — its first block's text, media and buttons are what the recipient sees. To send your own custom text with buttons that run a flow on tap, prefer send_message with `buttons: [{ text, flowId }]`; it needs no wrapper flow. The flow starts at its start block for every recipient, and any `{{var|name}}` inside it resolves against that recipient's own variable context. No deploy is needed — the runtime compiles the flow on demand — but the flow must already be applied (use the ids apply_actions returned). Contacts are targeted by contactId only (from list_contacts), 1 to 50 per call. Duplicates are collapsed. Each contact is dispatched independently: one bad id fails its own row in `results` and the others still go out, so read `sent`/`failed`, not just the absence of an error. BROADCAST and OPERATION flows are rejected — a broadcast flow runs in an audience scope (send it with its broadcast) and an operation runs in system context (use run_operation). For a large audience this is the WRONG tool: create a broadcast whose flow filter selects the audience, and launch that once. Requires the send_flow_to_contact permission. NOT idempotent and not reversible — every call reaches real people again and a sent message cannot be recalled. Confirm the flow and the exact recipient list with the user before calling, and never retry a timed-out call blindly.
send_message
textbotIdmediabuttonscontactIdplatformId
+1
Send a message to ONE contact right now, outside any flow. For reaching many contacts use a broadcast instead. Target the contact with contactId (globally unique — preferred), or w… Send a message to ONE contact right now, outside any flow. For reaching many contacts use a broadcast instead. Target the contact with contactId (globally unique — preferred), or with platformId (the platform-side id, e.g. the Telegram user id). platformId is NOT globally unique: it is unique only per bot, so the same Telegram user talking to two of your bots is two contacts sharing one platformId. Pass botId alongside it whenever the application has more than one bot; without botId the call succeeds only if exactly one contact in the application matches, and otherwise fails listing the candidate bots. `{{var|name}}` placeholders in the text resolve against that contact's variable context. Requires the manage_broadcasts permission. Media: pass up to 10 attachments as publicly reachable http(s) URLs; the text becomes the caption (max 1024 characters) and may be empty. Several attachments send as one album. The kind is inferred from the URL's file extension — override with type when the URL has none. Not supported for SDK bots. Buttons: up to 8, each carrying EXACTLY ONE destination — a `url` (http(s) or tg://) the recipient opens, or a `flowId`, an already-applied INTERACTIVE flow that runs for that recipient when they tap it. The two kinds mix freely in one keyboard, so a custom text with flow-wired buttons needs no wrapper flow. A flow button starts its flow from the start block with the recipient's own variable context, and re-runs on every tap. Broadcast and operation flows are rejected, as are flow buttons on SDK bots (taps never reach the runtime there). To send a WHOLE flow as the message instead of wiring one behind a button, use send_flow_to_contacts. Buttons cannot be combined with media; send those as two messages. Delivery is asynchronous: a successful response means the bot accepted the send, not that the platform delivered it (a broken media URL surfaces in the flow logs, not here). Unsubscribed contacts are rejected. NOT idempotent and not reversible — each call sends another message to a real person, and a sent message cannot be recalled. Confirm the recipient and text with the user before calling, and never retry a timed-out call blindly.
set_watched_groups
botIdgroups*applicationId
Replace a telegram_mtproto userbot's watched-groups list — the chats it monitors. Requires the manage_settings permission. SET semantics: send the COMPLETE desired list every time… Replace a telegram_mtproto userbot's watched-groups list — the chats it monitors. Requires the manage_settings permission. SET semantics: send the COMPLETE desired list every time (call list_watched_groups first and include existing entries you want to keep — omitting one removes it). Each entry needs a chatId (e.g. "-100…", for chats the account has joined) or a public username/t.me link; mode "joined" (default) processes a chat the account is in, "public_peek" (max 10, needs a username) polls a public chat without joining. The running userbot picks the change up within a few minutes, no restart. An empty list means "watch every joined chat" — NOT "watch nothing".
sync_dialog_contacts
botIdkindslimitapplicationId
Import a telegram_mtproto userbot's existing chats as contacts — DM partners, groups, and channels — so everything the account already talks to becomes a valid send_message target… Import a telegram_mtproto userbot's existing chats as contacts — DM partners, groups, and channels — so everything the account already talks to becomes a valid send_message target without waiting for each chat to message first. Requires the manage_broadcasts permission. Reads the account's dialog list live (the userbot must be connected; large accounts can take up to a minute) and creates missing contacts; existing contacts are untouched, so the call is idempotent. Pass kinds to narrow the import (e.g. ["group","channel"] to leave personal DMs out). Does NOT change the watched-groups list. botId may be omitted when the application has exactly one userbot.
update_application
nameisActiveapplicationIddefaultLanguageincomingMessageFlowIdincomingMessageBehavior
Update application-level settings (name, active state, default language, incoming-message behavior). Requires the manage_settings permission in that application. Only the fields yo… Update application-level settings (name, active state, default language, incoming-message behavior). Requires the manage_settings permission in that application. Only the fields you pass are changed; omitted fields keep their current value, so the call is idempotent. Returns the updated application.
update_contact
emailphonestatuslastNameusernamecontactId*
+3
Update a contact's profile fields and/or contact-variable values. Requires the manage_broadcasts permission. Only the fields you pass are changed — omitted fields keep their curren… Update a contact's profile fields and/or contact-variable values. Requires the manage_broadcasts permission. Only the fields you pass are changed — omitted fields keep their current value — so the call is idempotent. The variables map takes variable NAMES (or full folder paths when a name is ambiguous), not ids; an unknown name fails with 422 before anything is written. Variable writes propagate to the live bot immediately (the runtime's cached values are invalidated). Setting status to "unsubscribed" stops broadcasts and sequences for the contact.
validate_actions
read-only
flowIdactions*applicationIdconversationId