Skip to main content
skill CX Codex

pipefy-ai-agents — Claude Code skill for Pipefy AI agents

A skill that walks you through creating, updating and debugging conversational AI agents in Pipefy, behaviors included.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add pipefy/ai-toolkit --skill pipefy-ai-agents -a claude-code
Open on GitHub

What pipefy-ai-agents does

An agent in Pipefy is a conversational AI attached to a pipe. Each one carries an agent-level instruction plus 1 to 5 behaviors, and every behavior has its own trigger event, prompt and actions. The skill follows a discover, validate, create, verify loop: pull pipe metadata, check for existing agents, find valid events and action types, then build the config.

Seven operations sit behind it. get_ai_agents, get_ai_agent and validate_ai_agent_behaviors only read; create_ai_agent, update_ai_agent, delete_ai_agent and toggle_ai_agent_status write. Run the pre-flight behavior check before any create or update. Two things to watch: update_ai_agent is a full replace, not a patch, so always send the complete behaviors list, and delete_ai_agent is destructive — review and approve it first.

Agents start active; active=false creates a disabled one. An agent with no active behavior is treated as disabled by the API no matter what the flags say. The skill never guesses event IDs, phase IDs, field IDs or action types — those come from get_pipe, get_automation_events and get_automation_actions. For card_moved and field_updated you must pass event_params, otherwise the behavior fires on every occurrence. Execution logs live in the separate pipefy-observability skill.

One rule worth respecting: only create an agent when the user actually asked for AI. If it merely seems useful, ask first.

What it helps with

  • create a conversational AI agent on a pipe
  • validate behaviors before hitting the API
  • replace an agent config without dropping behaviors
  • activate or deactivate an agent explicitly
  • debug why an agent never reacts to cards

Example prompts for the agent

  • create an AI agent on this pipe that answers new cards
  • list every agent on the pipe with its behaviors
  • validate the agent behaviors before I update them

How to install pipefy-ai-agents

1Skills CLI (Vercel)

npx skills add pipefy/ai-toolkit --skill pipefy-ai-agents -a claude-code

2Manually: put the files in place

~/.claude/skills/pipefy-ai-agents/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/pipefy-ai-agents/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about pipefy-ai-agents

How do I install pipefy-ai-agents in Claude?

Run npx skills add pipefy/ai-toolkit --skill pipefy-ai-agents -a claude-code. The skill lands in ~/.claude/skills/pipefy-ai-agents/SKILL.md.

What does pipefy-ai-agents do?

It covers creating, reading, updating, deleting and troubleshooting conversational AI agents in Pipefy, including pre-flight behavior validation and pipe-scoped knowledge bases attached via dataSourceIds.

How is it different from pipefy-automations?

This skill handles conversational agents and their behaviors. Traditional automations and prompt-driven AI automations belong to pipefy-automations.

Source text

SKILL.md · Apache-2.0
---
name: pipefy-ai-agents
description: >
  Use this skill when the user wants to create, read, update, delete,
  or troubleshoot AI agents (conversational agents with behaviors).
  Includes pre-flight validation and pipe-scoped
  knowledge bases (list, plain text/document/data lookup CRUD, access probe) attached via dataSourceIds.
  For traditional automations and AI automations, see `pipefy-automations`.
tags: [pipefy, ai-agents, behaviors, conversational]
---

# AI Agents

Read only the reference for your active surface: [MCP](references/mcp.md) or [CLI](references/cli.md). 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`) sets `disabled_at` explicitly on create and the chained update.
- Routine `update_ai_agent` **preserves** disabled state — it does not intentionally reactivate. Prefer passing `disabled_at` from a prior `get_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_at` and `active` (`active` is true when `disabled_at` is null). On `get_ai_agent`, `disabledAt` null means active; `behaviors[].active` is 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 as `repo_uuid` in all AI-agent tools.
- `phases[].id` / `phases[].name` → needed for `move_card` actions.
- Fields via `get_start_form_fields(pipe_id)` and/or `get_phase_fields(phase_id)` → needed for `update_card` actions.

### 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` |
| 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) |
| 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

```json
{
  "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 literal `value`), `copy_from` (`value` is a `%{…}` template copying another field).
- **Input** field references (`%{field:<internal_id>}` in the behavior `instruction`, auto-populated into `referencedFieldIds` on create/update) are needed **only when** the AI must read card field values, not for every `fill_with_ai` (e.g. instruction-only, OCR/attachment, or knowledge-base context). When card inputs are needed and omitted, `card.fields` arrives empty at trigger time and the model may hallucinate. A wrong **numeric** input id is accepted silently (validate and create/update) and becomes a dead `referencedFieldId`; a wrong **slug** never resolves and is dropped by the digits-only extractor (unresolved token). Either way `card.fields` stays empty (same hallucination); confirm the id with `get_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`: set `destinationPhaseId: ""` 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

```json
// 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": ["[email protected]"], "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 carries `type` — use `providerId` for a custom (`byom`) provider and `systemProviderId` for a Pipefy-managed (`system`) one. `get_default_llm_provider` shows 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-level `data_source_ids`; the two are unioned. See [Knowledge bases](#knowledge-bases-data-sources) below for the create → attach flow.

```json
{
  "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_record` `fieldsAttributes` are **table** field IDs, so they are not checked against the pipe and surface a warning to verify with `get_table`; `send_email_template` metadata runs no pipe field-ID checks)
- Behavior structure passes Pydantic validation (including canonical `capabilitiesAttributes` shape and at most one of `providerId` / `systemProviderId`)
- `fieldsAttributes[].fieldId` values (outputs) are checked against start-form and phase fields, accepting both slug `id` and numeric `internal_id`. Instruction `%{field:...}` tokens (inputs) are **not** existence-checked: a missing id/slug still yields `valid: true`. Slug → numeric rewrite happens only on create/update, not here.
- Set `data_source_ids` (agent-level) to also check knowledge base membership: it is unioned with each behavior's `dataSourceIds` and checked against the pipe's knowledge bases. Unknown IDs are **warnings only** (`valid` stays 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**; set `active=false` to start disabled (see [Active lifecycle](#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_at` and `active` on the write response to verify agent enablement.
- **Partial failure (behaviors rejected)** → the SDK raises `AiAgentConfigureError`; use its `.agent_uuid` to recover the existing shell. The MCP response carries `agent_uuid`. Call `update_ai_agent` with the **full required payload**: `uuid`, `repo_uuid` (same pipe UUID used on create), `name`, `instruction`, and complete `behaviors` (full-replace, not patch). Do NOT create a second agent. The create shell is often disabled; update preserves that state — call `toggle_ai_agent_status` after 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; send 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

1. **Probe access** — `validate_knowledge_base_access(pipe_uuid)`. A green result proves read access only (`read_ai_agents`), never the `manage_ai_agents` entitlement writes need.
2. **Create the source** — `create_ai_knowledge_base_plain_text(pipe_uuid, name, content, description)`. Limits fail fast client-side: `content` 1-3500 chars, `description` 1-900 chars (both required). Keep the returned `id`.
3. **Attach** — add that `id` to a behavior's `dataSourceIds` (or the agent-level `data_source_ids`) when calling `create_ai_agent` / `update_ai_agent`. Validate first with `validate_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:

1. **Create** — `create_ai_knowledge_base_data_lookup(pipe_uuid, name, description, source_repo_id, output_fields, conditions)`. `source_repo_id` is 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_fields` takes 1-30 field IDs (field slugs plus static fields like `id`, `title`, `created_at`). Each condition needs `field` + `operator` (opaque backend string, e.g. `"eq"`, `"contains"`) and is either **static** (string `value` required) or **AI-filled** — the AI asks the user for the value at runtime:

   ```json
   [{"field": "customer_email", "operator": "eq", "usingFillWithAi": true,
     "inputName": "Customer email", "inputType": "text",
     "inputDescription": "The customer's email address"}]
   ```

2. **Attach** — keep the returned `id` and add it to `dataSourceIds`, exactly as for the other kinds. **Also keep the definition you sent**: reads never return `conditions`, so your copy is the only complete record of the lookup.
3. **Update replaces everything** — `update_ai_knowledge_base_data_lookup` requires `source_repo_id`, `output_fields`, and `conditions` on every call (the complete condition set, not a delta), and omitting `search_query` clears it. Only `name`/`description` keep 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 give `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.

```json
{
  "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.disabledAt` is null when active; `behaviors[].active` does not establish agent enablement.
- `validate_ai_agent_behaviors` reports 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; use `toggle_ai_agent_status` for 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. Before `update_ai_agent`, keep the `get_ai_agent` result and run `validate_ai_agent_behaviors`, which checks each `event_id` against `get_automation_events` — warning you when those events cannot be loaded, so an unverified `event_id` never reads as a clean result — and validates the `human_validation`/`mcp_tool` action metadata. After a failure, call `get_ai_agent` to see what is left, fix the payload, and send the full list again.
- **`RECORD_NOT_SAVED` does not name the cause.** `validate_ai_agent_behaviors` now names several of them — an unknown `event_id` (when the pipe's automation events load), and a `human_validation` or `mcp_tool` action 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_agent` returns a UUID but reports failure, call `update_ai_agent(uuid, repo_uuid, name, instruction, behaviors)` — all five are required. Reuse the create `repo_uuid`; send the full behaviors list. Do NOT create a second agent. Update preserves disabled state; use `toggle_ai_agent_status` to change enablement.
- **Cross-pipe `PERMISSION_DENIED`.** Behaviors with `create_connected_card` or cross-pipe `create_card` require the service account to be a member of **both** source and destination pipes. When it is not, the API returns a bare `PERMISSION_DENIED`. Recovery: `get_pipe_members` + `invite_members` on the destination pipe.
- **Phase transition rule on `move_card`.** Destination must be reachable from the source phase (`cards_can_be_moved_to_phases`). Both `validate_ai_agent_behaviors` and `create_ai_agent` / `update_ai_agent` enrich this error with `valid_destinations` and 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_agents` may return "Agent not found" on `get_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.message` may 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 the `errors` field 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](https://github.com/pipefy/ai-toolkit/blob/main/docs/mcp/tools/identifiers.md#ai-agents-and-knowledge-bases) — canonical map of which tool/argument expects slug vs `internal_id` vs uuid vs numeric id (AI agents scope by `repo_uuid` = pipe UUID).

© pipefy · Apache-2.0 · pipefy/ai-toolkit

Similar skills

All skills for Agent setup: memory, context, orchestration →

From the same set8

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor