AI Agents
Conversational AI agents attached to pipes. Each agent has an agent-level instruction and 1–5 behaviors, each with its own trigger event, prompt, and actions. 7 MCP tools.
For traditional automations and AI automations (prompt-driven), see skills/automations/pipefy-automations/SKILL.md.
Tools
| Tool (MCP) | CLI | Read-only | Purpose |
|---|---|---|---|
get_ai_agents |
pipefy agent list |
Yes | List AI agents for a pipe (repo_uuid = pipe UUID, not numeric id). |
get_ai_agent |
pipefy agent get |
Yes | Full agent config including behaviors. |
create_ai_agent |
pipefy agent create |
No | Create a new conversational agent (active by default; active=false / --inactive to start disabled). |
update_ai_agent |
pipefy agent update |
No | Full-replace (not patch). Always send complete behaviors. Preserves disabled state. |
delete_ai_agent |
pipefy agent delete |
No | (Two-step destructive)[^mcp-confirm] |
toggle_ai_agent_status |
pipefy agent toggle |
No | Explicit activate/deactivate (e.g. --inactive). |
validate_ai_agent_behaviors |
pipefy agent validate-behaviors |
Yes | Pre-flight check before create/update. |
[^mcp-confirm]: MCP two-step: echo confirmation_token from the preview with confirm=true. CLI: --yes.
The read tools (get_ai_agents, get_ai_agent, validate_ai_agent_behaviors) and the write tools (create/update/delete/toggle) are all remote-safe: available under the hosted (profile=remote) surface. create_ai_knowledge_base_document, create_llm_provider, and update_llm_provider take a local file and are local profile only — withheld on the hosted server.
Execution logs live in skills/observability/ (get_ai_agent_logs, get_ai_agent_log_details).
Active lifecycle
- Agents are active by default. Create-active clears the API default disabled shell via the configure update (omits
disabledAt). Create-inactive (active=false/--inactive) setsdisabled_atexplicitly on create and the chained update. - Routine
update_ai_agent/pipefy agent updatepreserves disabled state — it does not intentionally reactivate. Prefer passingdisabled_atfrom a priorget_ai_agent(disabledAt) /pipefy agent update --disabled-atto skip the preserve re-read; when omitted, the SDK re-reads and re-sends. - Explicit activate/deactivate:
toggle_ai_agent_status/pipefy agent toggle(--active/--inactive). - After create/update, confirm status from the response
disabled_at/activefields when present. To re-read viaget_ai_agent/pipefy agent get, use agentdisabledAt(null means active) — get does not expose write-envelopedisabled_at/active, andbehaviors[].activeis not agent enablement. Never assume inactive without that confirmation. An agent with no active behavior is disabled by the API regardless of the create/update enablement flags.
Creation workflow (discover → validate → create → verify)
Consent: create or suggest an AI agent only when the user explicitly asked for AI / an agent. If AI seems useful but was not requested, ask first — never introduce agents without being asked.
Never guess event IDs, phase IDs, action types, or field IDs.
1 — Get pipe metadata
Call get_pipe(pipe_id) and extract:
uuid→ use asrepo_uuidin all AI-agent tools.phases[].id/phases[].name→ needed formove_cardactions.- Fields via
get_start_form_fields(pipe_id)and/orget_phase_fields(phase_id)→ needed forupdate_cardactions.
2 — Check existing agents
get_ai_agents(repo_uuid) to avoid duplicates. To modify an existing agent, use update_ai_agent (not create). For full config, use get_ai_agent(uuid).
3 — Discover valid trigger events
get_automation_events(pipe_id). Common events:
Event (event_id value) |
event_params required |
Example |
|---|---|---|
| card_created | None | {} |
| card_moved | {"to_phase_id":"<phase_id>"} |
Fires only when card enters that phase. |
| field_updated | {"triggerFieldIds":["<field_id>", ...]} |
Fires only when those fields change. |
| manually_triggered | None | User clicks button on card. |
For card_moved and field_updated, you MUST include event_params. Omitting it makes the behavior fire on every occurrence.
4 — Discover valid action types
get_automation_actions(pipe_id). The 6 known actionType values and their required metadata:
Action (actionType value) |
metadata required |
|---|---|
| update_card | pipeId + fieldsAttributes (each entry needs fieldId + inputMode) |
| move_card | destinationPhaseId |
| create_card | pipeId + fieldsAttributes |
| create_connected_card | pipeId + fieldsAttributes (requires pipe relation) |
| create_table_record | tableId + fieldsAttributes (table field IDs; no pipeId) |
| send_email_template | emailTemplateId; optional allowTemplateModifications (bool) |
fieldId values for card actions accept slug or numeric internal_id; for create_table_record they are table field IDs (validate with get_table / get_table_record, not the pipe).
5 — Build the behavior dict
{
"name": "<descriptive name>",
"event_id": "<from step 3>",
"event_params": {},
"actionParams": {
"aiBehaviorParams": {
"instruction": "<prompt for the AI when this event fires>",
"actionsAttributes": [
{ "name": "<action label>", "actionType": "<from step 4>", "metadata": { } }
]
}
}
}
- Each behavior MUST have at least one action in
actionsAttributes. - Maximum 5 behaviors per agent.
- The MCP tool auto-injects
referenceIdand%{action:<uuid>}placeholders — do NOT generate these yourself. inputModeis required on everyfieldsAttributesentry (omitting it fails model validation). Values:fill_with_ai(AI writes the value into an output field),fixed_value(use the literalvalue),copy_from(valueis a%{…}template copying another field).- Input field references (
%{field:<internal_id>}in the behaviorinstruction, auto-populated intoreferencedFieldIdson create/update) are needed only when the AI must read card field values, not for everyfill_with_ai(e.g. instruction-only, OCR/attachment, or knowledge-base context). When card inputs are needed and omitted,card.fieldsarrives empty at trigger time and the model may hallucinate. A wrong numeric input id is accepted silently (validate and create/update) and becomes a deadreferencedFieldId; a wrong slug never resolves and is dropped by the digits-only extractor (unresolved token). Either waycard.fieldsstays empty (same hallucination); confirm the id withget_start_form_fields/get_phase_fields. Dotted connected-pipe refs (%{field:<parent>.<child>}) are not forwarded at runtime; to read a connected card field, use a field on the current pipe. - For
update_card: setdestinationPhaseId: ""when not moving the card.
Example identifiers (fictional)
Use real values from get_pipe / get_start_form_fields for your org. Placeholders below match unit-test fixtures in this repo. The syntax matters (pipeId, fieldId, %{field:<internal_id>}, inputMode) — the example digits do not; substitute each pipe's numeric internal_id and phase id.
| Role | Example value |
|---|---|
| Pipe (numeric repo id) | 987654321 |
Field internal_id |
900000101 |
Destination phase (move_card) |
900000201 |
Target pipe (create_card) |
900000301 |
Metadata examples
// update_card — output field fill_with_ai; input fields referenced in instruction
{ "pipeId": "987654321", "destinationPhaseId": "", "fieldsAttributes": [{ "fieldId": "900000101", "inputMode": "fill_with_ai", "value": "" }] }
// companion instruction (aiBehaviorParams.instruction), not metadata:
// "Read %{field:900000102} (title) and %{field:900000103} (description), then fill the category."
// move_card
{ "destinationPhaseId": "900000201", "pipeId": "", "fieldsAttributes": [] }
// create_card
{ "pipeId": "900000301", "fieldsAttributes": [{ "fieldId": "title", "inputMode": "fill_with_ai", "value": "" }] }
// create_table_record (fieldsAttributes are TABLE field IDs; no pipeId)
{ "tableId": "<table_id>", "fieldsAttributes": [{ "fieldId": "<table_field_id>", "inputMode": "fill_with_ai", "value": "" }] }
// send_email_template
{ "emailTemplateId": "<template_id>", "allowTemplateModifications": false }
5b — Optional: capabilities and LLM provider
Inside actionParams.aiBehaviorParams a behavior may also carry:
capabilitiesAttributes— advanced tools the behavior can use. Each entry is exactly{ "capabilityType": "<type>", "enabled": true|false }(both keys required, no extra keys — bare strings or{ "type": ... }are rejected).Product name capabilityTypeIDP / Intelligent Document Processing advanced_ocrCalculations & Analysis math_operationsWeb Search web_searchWeb Scraping web_scrapingMax effort max_effortcapabilityTypeis not checked against a fixed set — any value passes through and the API validates the enum on write, so new capabilities work without a toolkit update. Validation checks shape only, not entitlement — a capability may still require organization-level enablement to have any effect, so a green pre-flight does not guarantee the capability is active for the org.providerId/systemProviderId— pick the behavior's LLM provider. Set at most one (a behavior resolves to a single active provider). Discover valid IDs withget_llm_providers(CLI:pipefy ai-provider list): each provider carriestype— useproviderIdfor a custom (byom) provider andsystemProviderIdfor a Pipefy-managed (system) one.get_default_llm_providershows what a behavior falls back to when neither is set. IDs are also visible in the organization's AI settings in the Pipefy UI.Bring your own model (custom provider). To back a behavior with your own vendor credentials, create a custom provider first, then use its
idasproviderId:validate_llm_provider_access(confirm read access — writes need the strongermanage_ai_providersorg permission and an eligible plan, so a write may still be denied) →create_llm_providerwith the configuration in a local JSON file (configuration_file_path; never inline — secrets are never logged or returned; the file'sproviderkey selects the vendor).create_llm_providerandupdate_llm_providerare local profile only (configuration_file_pathhas no meaning on the hosted server; both tools are withheld there). On the hosted URL, attach an existing provider viaget_llm_providersandproviderId/systemProviderId, or create the provider from the CLI / Quick-install path. Manage a local-profile provider withupdate_llm_provider(send the full configuration; leave the__REDACTED__placeholders fromget_llm_providersin place to keep existing secrets, or put a new value to rotate one),set_llm_provider_active_status, anddelete_llm_provider(checkget_llm_provider_dependenciesfirst). Set the organization default withset_default_llm_provider(exactly one ofprovider_id/system_provider_id) or clear it withreset_default_llm_provider. CLI:pipefy ai-provider create/update/delete/set-active-status/default set/default reset.dataSourceIds— knowledge base sources the behavior can draw on. Each ID is a knowledge base item ID fromget_ai_knowledge_bases(CLI:pipefy kb list). Agents also carry an agent-leveldata_source_ids; the two are unioned. See Knowledge bases below for the create → attach flow.
{
"instruction": "Extract totals from the attached invoice.",
"capabilitiesAttributes": [{ "capabilityType": "advanced_ocr", "enabled": true }],
"actionsAttributes": [ /* ... */ ]
}
6 — Validate (recommended for complex behaviors)
validate_ai_agent_behaviors(pipe_id, behaviors) checks:
- Output field IDs (
fieldsAttributes[].fieldId) exist in the pipe - Phase IDs exist
- Pipe relations exist for
create_connected_card - Action types are valid (the 6 in
KNOWN_AI_ACTION_TYPES;create_table_recordfieldsAttributesare table field IDs, so they are not checked against the pipe and surface a warning to verify withget_table;send_email_templatemetadata runs no pipe field-ID checks) - Behavior structure passes Pydantic validation (including canonical
capabilitiesAttributesshape and at most one ofproviderId/systemProviderId) fieldsAttributes[].fieldIdvalues (outputs) are checked against start-form and phase fields, accepting both slugidand numericinternal_id. Instruction%{field:...}tokens (inputs) are not existence-checked: a missing id/slug still yieldsvalid: true. Slug → numeric rewrite happens only on create/update, not here.- Pass
data_source_ids(agent-level) to also check knowledge base membership: it is unioned with each behavior'sdataSourceIdsand checked against the pipe's knowledge bases. Unknown IDs are warnings only (validstays true); if the knowledge base list cannot be read, a single warning is added and the check is skipped.
strict_unknown_action_types (default true): an actionType outside the known 6 is reported in problems (blocking). Set false to demote unknown action types to warnings only, so valid stays true. CLI: --strict (default) / --no-strict on agent validate-behaviors, agent create, and agent update.
7 — Create the agent
create_ai_agent with name, repo_uuid, instruction, and behaviors. One-call creation is preferred — avoids partial agent shells. Agents are active by default; pass active=false (MCP) or --inactive (CLI) to start disabled (see Active lifecycle).
The CLI agent create / agent update require --pipe (numeric pipe id) and run validate_ai_agent_behaviors automatically as a pre-flight, blocking the write when problems are found and surfacing warnings under a preflight key. The MCP tools do not auto-preflight, so call validate_ai_agent_behaviors yourself (step 6) before create_ai_agent / update_ai_agent. CLI flags: --repo-uuid, --name, --instruction, --behaviors (JSON array), --data-sources (JSON array); create also: --active / --inactive (update has no status flags — use agent toggle); agent validate-behaviors instead takes --data-source-id (repeatable).
On create/update, slug fieldId values are resolved to numeric internal_id, %{field:<slug>} is rewritten to %{field:<internal_id>}, and referencedFieldIds is auto-populated when applicable.
8 — Handle responses
- Success with
agent_uuid→ confirmdisabled_at/activeon the response (active whendisabled_atis null). - Partial failure (UUID returned, behaviors rejected) → call
update_ai_agentwith the full required payload:uuid,repo_uuid(same pipe UUID used on create),name,instruction, and completebehaviors(full-replace, not patch). Do NOT create a second agent. The create shell is often disabled (disabled_aton the partial-failure envelope); update preserves that state — calltoggle_ai_agent_statusafter a successful recovery update if you need the agent active. - Failure without UUID → validation or API error. Trust the hint text in the enriched error.
9 — Verify
get_ai_agent(uuid) to confirm behaviors match expectations and, when the write response is unclear, re-read agent enablement via disabledAt (null means active). Prefer create/update response disabled_at / active when present. Do not treat behaviors[].active as agent enablement; never assume inactive without confirmation.
Knowledge bases (data sources)
Knowledge bases are pipe-scoped data sources an agent draws on. Attach one by putting its ID in a behavior's dataSourceIds (or the agent-level data_source_ids). All knowledge base operations are scoped by the pipe UUID (pipe_uuid), not the numeric pipe ID — get_pipe returns the uuid.
| Tool (MCP) | CLI | Read-only | Purpose |
|---|---|---|---|
get_ai_knowledge_bases |
pipefy kb list |
Yes | List every item on a pipe (plain texts, documents, data lookups); each has an id for dataSourceIds and a type (knowledge_base_plain_texts, knowledge_base_documents, or data_lookups). |
get_ai_knowledge_base_plain_text |
pipefy kb plain-text get |
Yes | Fetch one plain text with its content. |
create_ai_knowledge_base_plain_text |
pipefy kb plain-text create |
No | Create a plain text (name, content 1-3500, description 1-900 — all required). |
update_ai_knowledge_base_plain_text |
pipefy kb plain-text update |
No | Partial update; pass at least one of name/content/description. |
delete_ai_knowledge_base_plain_text |
pipefy kb plain-text delete |
No | (Two-step destructive)[^mcp-confirm] |
get_ai_knowledge_base_document |
pipefy kb document get |
Yes | Fetch one document's metadata (content is the stored URL, not text). |
create_ai_knowledge_base_document |
pipefy kb document create |
No | Upload a local PDF in one shot (file_path/--file, name, description 1-900). .pdf + 20 MiB cap client-side; indexing is async. file_path is local profile only — withheld on the hosted server (no hosted-safe source). |
update_ai_knowledge_base_document |
pipefy kb document update |
No | Metadata-only update (name/description); no file replacement. |
delete_ai_knowledge_base_document |
pipefy kb document delete |
No | (Two-step destructive)[^mcp-confirm] |
get_ai_knowledge_base_data_lookup |
pipefy kb data-lookup get |
Yes | Fetch one data lookup; the payload never includes conditions — keep the definition client-side. |
create_ai_knowledge_base_data_lookup |
pipefy kb data-lookup create |
No | Create a data lookup (name, description 1-900, source_repo_id numeric pipe ID, output_fields 1-30, conditions — all required). |
update_ai_knowledge_base_data_lookup |
pipefy kb data-lookup update |
No | Full replacement: resend source_repo_id/output_fields/conditions every call; omitted search_query clears it; only name/description are partial. |
delete_ai_knowledge_base_data_lookup |
pipefy kb data-lookup delete |
No | (Two-step destructive)[^mcp-confirm] |
validate_knowledge_base_access |
pipefy kb validate-access |
Yes | Probe read access before writes. |
Flow: validate-access → create plain text → attach
- Probe access —
validate_knowledge_base_access(pipe_uuid)(CLI:pipefy kb validate-access). A green result proves read access only (read_ai_agents), never themanage_ai_agentsentitlement writes need. The CLI create/update commands gate on this automatically; MCP callers should probe first (create/update do not auto-probe). - Create the source —
create_ai_knowledge_base_plain_text(pipe_uuid, name, content, description). Limits fail fast client-side:content1-3500 chars,description1-900 chars (both required). Keep the returnedid. - Attach — add that
idto a behavior'sdataSourceIds(or the agent-leveldata_source_ids) when callingcreate_ai_agent/update_ai_agent. Validate first withvalidate_ai_agent_behaviors(pipe_id, behaviors, data_source_ids=[...])— unknown IDs surface as warnings.
For a PDF document instead of plain text, use create_ai_knowledge_base_document(pipe_uuid, name, description, file_path) (CLI: pipefy kb document create --file …) at step 2. That tool is local profile only (file_path is a file on the machine running the MCP server; it is withheld on the hosted URL). On the hosted server, create a plain-text source instead, or use the CLI / Quick-install path. Locally, it uploads the PDF in one shot; .pdf and the 20 MiB cap are enforced client-side, and indexing is asynchronous (the document may not be searchable immediately). The rest of the flow is identical — keep the returned id and attach it.
Data lookups: create with an AI-filled condition → attach → update (full replacement)
A data lookup lets the agent search cards in a source pipe by conditions and return selected field values. Same flow as above at step 2, with three rules of its own:
Create —
create_ai_knowledge_base_data_lookup(pipe_uuid, name, description, source_repo_id, output_fields, conditions)(CLI:pipefy kb data-lookup create --source-repo-id … --output-fields '[…]' --conditions '[…]').source_repo_idis the numeric ID of the source pipe (a UUID is accepted by the API but the lookup then breaks when the agent runs it).output_fieldstakes 1-30 field IDs (field slugs plus static fields likeid,title,created_at). Each condition needsfield+operator(opaque backend string, e.g."eq","contains") and is either static (stringvaluerequired) or AI-filled — the AI asks the user for the value at runtime:[{"field": "customer_email", "operator": "eq", "usingFillWithAi": true, "inputName": "Customer email", "inputType": "text", "inputDescription": "The customer's email address"}]Attach — keep the returned
idand add it todataSourceIds, exactly as for the other kinds. Also keep the definition you sent: reads never returnconditions, so your copy is the only complete record of the lookup.Update replaces everything —
update_ai_knowledge_base_data_lookuprequiressource_repo_id,output_fields, andconditionson every call (the complete condition set, not a delta), and omittingsearch_queryclears it. Onlyname/descriptionkeep their stored values when omitted.
Token normalization & slug resolution
Instructions accept five token aliases — all normalize to canonical %{field:<internal_id>}:
| Form | Behavior |
|---|---|
%{<internal_id>} |
Canonical short form. |
{<internal_id>} |
Bare; auto-prefixed with %. |
{field:<internal_id>} |
Bare-with-prefix; auto-%. |
{field:<slug>} |
Bare slug; resolved to numeric when behavior action carries pipeId. |
%{field:<internal_id>} |
Canonical full form. |
%{field:<slug>} is rewritten to %{field:<internal_id>} when an action in the behavior supplies pipeId. If the Pipefy UI shows plain text instead of chips in token slots, the payload probably still has non-canonical tokens.
Template params / placeholders
Per behavior you can pass template_params (or placeholders) with str → str values and use {{name}} in any string (instruction, metadata IDs, etc.). Optionally set instruction_template instead of aiBehaviorParams.instruction — the tool interpolates and writes the final instruction before the API call. These keys are stripped before validation.
{
"name": "Classify card",
"event_id": "card_created",
"instruction_template": "Read {{field_ref}} and classify the card.",
"template_params": { "field_ref": "%{field:900000101}" },
"actionParams": {
"aiBehaviorParams": {
"actionsAttributes": [
{
"name": "Fill classification",
"actionType": "update_card",
"metadata": { "pipeId": "{{pipe}}", "fieldsAttributes": [{ "fieldId": "{{class_field}}", "inputMode": "fill_with_ai", "value": "" }] }
}
]
}
},
"placeholders": { "pipe": "987654321", "class_field": "900000101" }
}
template_params and placeholders merge (placeholders wins on conflict).
Naming differences (UI vs API)
| Pipefy UI | API / Tool field |
|---|---|
| Description (agent creation step 1) | instruction (agent-level) |
| Instruction / Prompt (per behavior) | actionParams.aiBehaviorParams.instruction |
| Pipe UUID | repo_uuid (from get_pipe().uuid, NOT the numeric id) |
Success criteria
- Create/update response shows the expected
disabled_at/active(active whendisabled_atis null). If confirming viaget_ai_agent, use agentdisabledAt(null means active) — not write-envelope keys and notbehaviors[].active. validate_ai_agent_behaviorsreports no errors before creation.- Agent appears in the Pipefy UI under the pipe's AI settings.
Failure modes
update_ai_agentis full-replace, not patch. Fetch existing behaviors withget_ai_agentfirst, merge, then update — otherwise existing behaviors are silently dropped. Update never reactivates a disabled agent — usetoggle_ai_agent_status/pipefy agent togglefor that.- Behavior save is all-or-nothing (
RECORD_NOT_SAVED). One invalid behavior rejects the entire list. The MCP tool auto-validates the payload on failure; if structurally correct, the error indicates a pipe-level restriction (not your payload). Inform the user this pipe does not support AI agent behaviors and suggest alternatives. - Partial-failure recovery. If
create_ai_agentreturns a UUID but reports failure, callupdate_ai_agent(uuid, repo_uuid, name, instruction, behaviors)— all five are required. Reuse the createrepo_uuid; send the full behaviors list. Do NOT create a second agent. Update preserves disabled state; usetoggle_ai_agent_status/pipefy agent toggleto change enablement. - Cross-pipe
PERMISSION_DENIED. Behaviors withcreate_connected_cardor cross-pipecreate_cardrequire the service account to be a member of both source and destination pipes. When it is not, the API returns a barePERMISSION_DENIED. Recovery:get_pipe_members+invite_memberson the destination pipe. - Phase transition rule on
move_card. Destination must be reachable from the source phase (cards_can_be_moved_to_phases). Bothvalidate_ai_agent_behaviorsandcreate_ai_agent/update_ai_agentenrich this error withvalid_destinationsand a hint that transition rules are editable in the Pipefy UI only. - Maximum 5 behaviors per agent. Adding a 6th rejects the whole save.
- Ghost agents. An agent listed by
get_ai_agentsmay return "Agent not found" onget_ai_agent— a Pipefy backend artifact, persists across sessions, do not retry. - GraphQL error hints. When a dedicated read tool returns permission-denied or not-found, the
error.messagemay cite concrete tools (e.g."Use 'get_ai_agents' to list agents..."). Trust the hint; don't improvise alternative flows. - Validation rejections. Common issues: invalid
trigger_event, prompt too long, missing required action config. Read theerrorsfield per behavior. delete_ai_agentfirst call returns preview. Expected. Show the preview to the user and get their approval, then call withconfirm=trueand the preview'sconfirmation_token.
See also
- skills/automations/pipefy-automations/SKILL.md — traditional automations and AI automations (different from AI agents).
- skills/observability/pipefy-observability/SKILL.md — agent execution logs and credit usage.
- skills/introspection/pipefy-introspection/SKILL.md — Recipe 2 inspects full behavior config via
execute_graphql. docs/mcp/tools/identifiers.md#ai-agents-and-knowledge-bases— canonical map of which tool/argument expects slug vsinternal_idvs uuid vs numeric id (AI agents scope byrepo_uuid= pipe UUID).