Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
AI Agents
Read only the reference for your active surface: MCP or CLI. The workflows below use shared operation names and arguments.
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.
For traditional automations and AI automations (prompt-driven), see pipefy-automations.
Tools
| Operation | Read-only | Purpose |
|---|---|---|
get_ai_agents | Yes | List AI agents for a pipe (repo_uuid = pipe UUID, not numeric id). |
get_ai_agent | Yes | Full agent config including behaviors. |
create_ai_agent | No | Create a new conversational agent (active by default; active=false to start disabled). |
update_ai_agent | No | Full-replace (not patch). Always send complete behaviors. Preserves disabled state. |
delete_ai_agent | No | Destructive; review and approve first. |
toggle_ai_agent_status | No | Explicit activate/deactivate. |
validate_ai_agent_behaviors | Yes | Pre-flight check before create/update. |
Execution logs live in pipefy-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) setsdisabled_atexplicitly on create and the chained update. - Routine
update_ai_agentpreserves disabled state — it does not intentionally reactivate. Prefer passingdisabled_atfrom a priorget_ai_agent(disabledAt) to skip the preserve re-read; when omitted, the SDK re-reads and re-sends. - Explicit activate/deactivate:
toggle_ai_agent_status. - After create/update, check the response's
disabled_atandactive(activeis true whendisabled_atis null). Onget_ai_agent,disabledAtnull means active;behaviors[].activeis not agent enablement. An agent with no active behavior is disabled by the API regardless of 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). 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 |
| createconnectedcard | pipeId + fieldsAttributes (requires pipe relation) |
| createtablerecord | tableId + fieldsAttributes (table field IDs; no pipeId) |
| sendemailtemplate | emailTemplateId; optional allowTemplateModifications (bool) |
| human_validation | emails (recipient list) and title. The API accepts either one alone but rejects empty metadata |
| mcp_tool | mcpServerId + toolName; optional toolInputs, each with name and source: fixed_value with a value, or card_field with a fieldId |
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.
- `inputMode` is required on every `fieldsAttributes` entry (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", "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 }
// human_validation
{ "emails": ["reviewer@example.com"], "title": "Review this card" }
// mcp_tool (mcpServerId is an MCP server registered on the pipe)
{ "mcpServerId": "<mcp_server_id>", "toolName": "<tool_name>", "toolInputs": [{ "name": "query", "source": "fixed_value", "value": "acme" }, { "name": "email", "source": "card_field", "fieldId": "customer_email" }] }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 | capabilityType |
|---|---|
| IDP / Intelligent Document Processing | advanced_ocr |
| Calculations & Analysis | math_operations |
| Web Search | web_search |
| Web Scraping | web_scraping |
| Max effort | max_effort |
capabilityType is 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 with
get_llm_providers: 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). First probe validate_llm_provider_access: read access does not prove the stronger manage_ai_providers permission or eligible plan required for writes. Create a provider with configuration_file_path (a local JSON file) and use its id as providerId. For update_llm_provider, send the full configuration: retain __REDACTED__ placeholders to preserve existing secrets or supply new values to rotate them. Check get_llm_provider_dependencies before delete_llm_provider; use set_llm_provider_active_status for activation. set_default_llm_provider takes exactly one of provider_id / system_provider_id; reset_default_llm_provider clears the default.
- `dataSourceIds` — knowledge base sources the behavior can draw on. Each ID is a knowledge base item ID from
get_ai_knowledge_bases. 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 (
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 KNOWN_AI_ACTION_TYPES is reported in problems (blocking). Set false to demote unknown action types to warnings only, so valid stays true.
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 to start disabled (see Active lifecycle).
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. The SDK injects action referenceId values and %{action:<uuid>} placeholders for every surface; do not generate them yourself.
8 — Handle responses
- Success with `agent_uuid` → check
disabled_atandactiveon the write response to verify agent enablement. - Partial failure (behaviors rejected) → the SDK raises
AiAgentConfigureError; use its.agent_uuidto recover the existing shell. The MCP response carriesagent_uuid. Callupdate_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; 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 and enablement (disabledAt null means active). 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.
| Operation | Read-only | Purpose |
|---|---|---|
get_ai_knowledge_bases | 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 | Yes | Fetch one plain text with its content. |
create_ai_knowledge_base_plain_text | No | Create a plain text (name, content 1-3500, description 1-900 — all required). |
update_ai_knowledge_base_plain_text | No | Partial update; pass at least one of name/content/description. |
delete_ai_knowledge_base_plain_text | No | Destructive; review and approve first. |
get_ai_knowledge_base_document | Yes | Fetch one document's metadata (content is the stored URL, not text). |
create_ai_knowledge_base_document | No | Upload a PDF (name, description 1-900, file_path). .pdf + 20 MiB cap; indexing is asynchronous. |
update_ai_knowledge_base_document | No | Metadata-only update (name/description); no file replacement. |
delete_ai_knowledge_base_document | No | Destructive; review and approve first. |
get_ai_knowledge_base_data_lookup | Yes | Fetch one data lookup; the payload never includes conditions — keep the definition client-side. |
create_ai_knowledge_base_data_lookup | 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 | 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 | No | Destructive; review and approve first. |
validate_knowledge_base_access | Yes | Probe read access before writes. |
Flow: validate-access → create plain text → attach
- Probe access —
validate_knowledge_base_access(pipe_uuid). A green result proves read access only (read_ai_agents), never themanage_ai_agentsentitlement writes need. - 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, use create_ai_knowledge_base_document with pipe_uuid, name, description, and file_path at step 2. Only .pdf is accepted, with a 20 MiB cap. Indexing is asynchronous, so the document may not be searchable immediately. 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).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 final instruction is interpolated and written before the API call (by the MCP tools, the CLI, and the SDK's CreateAiAgentInput / UpdateAiAgentInput). These keys are stripped during 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
- Agent enablement matches the intended state:
get_ai_agent.disabledAtis null when active;behaviors[].activedoes not establish agent enablement. validate_ai_agent_behaviorsreports no errors before creation.- Agent appears in the Pipefy UI under the pipe's AI settings.
Failure modes
- `update_ai_agent` is full-replace, not patch. Read the agent with
get_ai_agent, change what you need, and send the complete behaviors list back. A behavior you leave out is deleted. You can send the read behaviors back as they are; the ids and%{action:…}lines they carry are replaced on save. Update never reactivates a disabled agent; usetoggle_ai_agent_statusfor that. - A rejected update is not rolled back. Pipefy applies the name and instruction and removes the current behaviors before it saves the new list. When it then rejects the list (
RECORD_NOT_SAVED), the agent is left with no behaviors, or only part of the new list, and stays enabled. Beforeupdate_ai_agent, keep theget_ai_agentresult and runvalidate_ai_agent_behaviors, which checks eachevent_idagainstget_automation_events— warning you when those events cannot be loaded, so an unverifiedevent_idnever reads as a clean pass — and validates thehuman_validation/mcp_toolaction metadata. After a failure, callget_ai_agentto see what is left, fix the payload, and send the full list again. - `RECORD_NOT_SAVED` does not name the cause.
validate_ai_agent_behaviorsnow names several of them — an unknownevent_id(when the pipe's automation events load), and ahuman_validationormcp_toolaction with missing metadata — so run it first. The same message still covers payload errors the pre-flight cannot see, so rule those named causes out before you conclude the pipe does not support AI agent behaviors. - 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_statusto change enablement. - Cross-pipe `PERMISSION_DENIED`. Behaviors with
create_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.
See also
pipefy-automations— traditional automations and AI automations (different from AI agents).pipefy-observability— agent execution logs and credit usage.- docs/mcp/tools/identifiers.md#ai-agents-and-knowledge-bases — canonical map of which tool/argument expects slug vs
internal_idvs uuid vs numeric id (AI agents scope byrepo_uuid= pipe UUID).

